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  /*
17    Copyright (C) 2004-2007 University Corporation for Advanced Internet Development, Inc.
18    Copyright (C) 2004-2007 The University Of Chicago
19  
20    Licensed under the Apache License, Version 2.0 (the "License");
21    you may not use this file except in compliance with the License.
22    You may obtain a copy of the License at
23  
24      http://www.apache.org/licenses/LICENSE-2.0
25  
26    Unless required by applicable law or agreed to in writing, software
27    distributed under the License is distributed on an "AS IS" BASIS,
28    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29    See the License for the specific language governing permissions and
30    limitations under the License.
31  */
32  
33  package edu.internet2.middleware.grouper;
34  
35  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.excludeDescription;
36  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.excludeDisplayExtensionSuffix;
37  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.excludeExtensionSuffix;
38  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.includeDescription;
39  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.includeDisplayExtensionSuffix;
40  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.includeExtensionSuffix;
41  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.overallDescription;
42  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.systemOfRecordAndIncludesDescription;
43  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.systemOfRecordAndIncludesDisplayExtensionSuffix;
44  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.systemOfRecordAndIncludesExtensionSuffix;
45  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.systemOfRecordDescription;
46  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.systemOfRecordDisplayExtensionSuffix;
47  import static edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook.systemOfRecordExtensionSuffix;
48  
49  import java.sql.Timestamp;
50  import java.util.Collection;
51  import java.util.Collections;
52  import java.util.Date;
53  import java.util.HashMap;
54  import java.util.HashSet;
55  import java.util.Iterator;
56  import java.util.LinkedHashMap;
57  import java.util.LinkedHashSet;
58  import java.util.Map;
59  import java.util.Set;
60  
61  import org.apache.commons.lang.StringUtils;
62  import org.apache.commons.lang.builder.EqualsBuilder;
63  import org.apache.commons.lang.builder.HashCodeBuilder;
64  import org.apache.commons.lang.builder.ToStringBuilder;
65  import org.apache.commons.lang.time.StopWatch;
66  import org.apache.commons.logging.Log;
67  import org.hibernate.CallbackException;
68  import org.hibernate.Session;
69  import org.hibernate.classic.Lifecycle;
70  
71  import edu.internet2.middleware.grouper.annotations.GrouperIgnoreClone;
72  import edu.internet2.middleware.grouper.annotations.GrouperIgnoreDbVersion;
73  import edu.internet2.middleware.grouper.annotations.GrouperIgnoreFieldConstant;
74  import edu.internet2.middleware.grouper.app.loader.GrouperLoader;
75  import edu.internet2.middleware.grouper.attr.AttributeDef;
76  import edu.internet2.middleware.grouper.attr.AttributeDefName;
77  import edu.internet2.middleware.grouper.attr.AttributeDefType;
78  import edu.internet2.middleware.grouper.attr.assign.AttributeAssign;
79  import edu.internet2.middleware.grouper.attr.assign.AttributeAssignEffMshipDelegate;
80  import edu.internet2.middleware.grouper.attr.assign.AttributeAssignGroupDelegate;
81  import edu.internet2.middleware.grouper.attr.assign.AttributeAssignMembershipDelegate;
82  import edu.internet2.middleware.grouper.attr.assign.AttributeAssignable;
83  import edu.internet2.middleware.grouper.attr.finder.AttributeAssignFinder;
84  import edu.internet2.middleware.grouper.attr.value.AttributeAssignValue;
85  import edu.internet2.middleware.grouper.attr.value.AttributeAssignValueResult;
86  import edu.internet2.middleware.grouper.attr.value.AttributeValueDelegate;
87  import edu.internet2.middleware.grouper.audit.AuditEntry;
88  import edu.internet2.middleware.grouper.audit.AuditTypeBuiltin;
89  import edu.internet2.middleware.grouper.cfg.GrouperConfig;
90  import edu.internet2.middleware.grouper.changeLog.ChangeLogEntry;
91  import edu.internet2.middleware.grouper.changeLog.ChangeLogLabels;
92  import edu.internet2.middleware.grouper.changeLog.ChangeLogTypeBuiltin;
93  import edu.internet2.middleware.grouper.ddl.GrouperDdlUtils;
94  import edu.internet2.middleware.grouper.entity.Entity;
95  import edu.internet2.middleware.grouper.entity.EntityUtils;
96  import edu.internet2.middleware.grouper.exception.AttributeDefNotFoundException;
97  import edu.internet2.middleware.grouper.exception.AttributeNotFoundException;
98  import edu.internet2.middleware.grouper.exception.CompositeNotFoundException;
99  import edu.internet2.middleware.grouper.exception.GrantPrivilegeAlreadyExistsException;
100 import edu.internet2.middleware.grouper.exception.GrantPrivilegeException;
101 import edu.internet2.middleware.grouper.exception.GroupAddException;
102 import edu.internet2.middleware.grouper.exception.GroupDeleteException;
103 import edu.internet2.middleware.grouper.exception.GroupModifyAlreadyExistsException;
104 import edu.internet2.middleware.grouper.exception.GroupModifyException;
105 import edu.internet2.middleware.grouper.exception.GroupNotFoundException;
106 import edu.internet2.middleware.grouper.exception.GrouperException;
107 import edu.internet2.middleware.grouper.exception.GrouperSessionException;
108 import edu.internet2.middleware.grouper.exception.GrouperValidationException;
109 import edu.internet2.middleware.grouper.exception.InsufficientPrivilegeException;
110 import edu.internet2.middleware.grouper.exception.MemberAddAlreadyExistsException;
111 import edu.internet2.middleware.grouper.exception.MemberAddException;
112 import edu.internet2.middleware.grouper.exception.MemberDeleteAlreadyDeletedException;
113 import edu.internet2.middleware.grouper.exception.MemberDeleteException;
114 import edu.internet2.middleware.grouper.exception.MemberNotFoundException;
115 import edu.internet2.middleware.grouper.exception.MembershipAlreadyExistsException;
116 import edu.internet2.middleware.grouper.exception.MembershipNotFoundException;
117 import edu.internet2.middleware.grouper.exception.RevokePrivilegeAlreadyRevokedException;
118 import edu.internet2.middleware.grouper.exception.RevokePrivilegeException;
119 import edu.internet2.middleware.grouper.exception.SchemaException;
120 import edu.internet2.middleware.grouper.exception.StemAddException;
121 import edu.internet2.middleware.grouper.exception.StemNotFoundException;
122 import edu.internet2.middleware.grouper.exception.UnableToPerformAlreadyExistsException;
123 import edu.internet2.middleware.grouper.exception.UnableToPerformException;
124 import edu.internet2.middleware.grouper.group.TypeOfGroup;
125 import edu.internet2.middleware.grouper.hibernate.AuditControl;
126 import edu.internet2.middleware.grouper.hibernate.GrouperTransaction;
127 import edu.internet2.middleware.grouper.hibernate.GrouperTransactionHandler;
128 import edu.internet2.middleware.grouper.hibernate.GrouperTransactionType;
129 import edu.internet2.middleware.grouper.hibernate.HibUtilsMapping;
130 import edu.internet2.middleware.grouper.hibernate.HibernateHandler;
131 import edu.internet2.middleware.grouper.hibernate.HibernateHandlerBean;
132 import edu.internet2.middleware.grouper.hibernate.HibernateSession;
133 import edu.internet2.middleware.grouper.hooks.GroupHooks;
134 import edu.internet2.middleware.grouper.hooks.beans.HooksGroupBean;
135 import edu.internet2.middleware.grouper.hooks.examples.GroupTypeTupleIncludeExcludeHook;
136 import edu.internet2.middleware.grouper.hooks.logic.GrouperHookType;
137 import edu.internet2.middleware.grouper.hooks.logic.GrouperHooksUtils;
138 import edu.internet2.middleware.grouper.hooks.logic.HookVeto;
139 import edu.internet2.middleware.grouper.hooks.logic.VetoTypeGrouper;
140 import edu.internet2.middleware.grouper.instrumentation.InstrumentationDataBuiltinTypes;
141 import edu.internet2.middleware.grouper.instrumentation.InstrumentationThread;
142 import edu.internet2.middleware.grouper.internal.dao.GrouperDAOException;
143 import edu.internet2.middleware.grouper.internal.dao.QueryOptions;
144 import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GrouperVersioned;
145 import edu.internet2.middleware.grouper.internal.util.GrouperUuid;
146 import edu.internet2.middleware.grouper.internal.util.Quote;
147 import edu.internet2.middleware.grouper.internal.util.U;
148 import edu.internet2.middleware.grouper.log.EventLog;
149 import edu.internet2.middleware.grouper.member.SearchStringEnum;
150 import edu.internet2.middleware.grouper.member.SortStringEnum;
151 import edu.internet2.middleware.grouper.membership.MembershipType;
152 import edu.internet2.middleware.grouper.misc.CompositeType;
153 import edu.internet2.middleware.grouper.misc.E;
154 import edu.internet2.middleware.grouper.misc.GrouperDAOFactory;
155 import edu.internet2.middleware.grouper.misc.GrouperHasContext;
156 import edu.internet2.middleware.grouper.misc.GrouperObject;
157 import edu.internet2.middleware.grouper.misc.GrouperSessionHandler;
158 import edu.internet2.middleware.grouper.misc.GrouperVersion;
159 import edu.internet2.middleware.grouper.misc.M;
160 import edu.internet2.middleware.grouper.misc.Owner;
161 import edu.internet2.middleware.grouper.misc.SaveMode;
162 import edu.internet2.middleware.grouper.permissions.PermissionRoleDelegate;
163 import edu.internet2.middleware.grouper.permissions.role.Role;
164 import edu.internet2.middleware.grouper.permissions.role.RoleHierarchyType;
165 import edu.internet2.middleware.grouper.permissions.role.RoleInheritanceDelegate;
166 import edu.internet2.middleware.grouper.permissions.role.RoleSet;
167 import edu.internet2.middleware.grouper.privs.AccessPrivilege;
168 import edu.internet2.middleware.grouper.privs.AccessResolver;
169 import edu.internet2.middleware.grouper.privs.Privilege;
170 import edu.internet2.middleware.grouper.privs.PrivilegeHelper;
171 import edu.internet2.middleware.grouper.rules.RuleCheckType;
172 import edu.internet2.middleware.grouper.rules.RuleDefinition;
173 import edu.internet2.middleware.grouper.rules.RuleEngine;
174 import edu.internet2.middleware.grouper.rules.RuleThenEnum;
175 import edu.internet2.middleware.grouper.rules.RuleUtils;
176 import edu.internet2.middleware.grouper.rules.beans.RulesMembershipBean;
177 import edu.internet2.middleware.grouper.rules.beans.RulesPrivilegeBean;
178 import edu.internet2.middleware.grouper.subj.GrouperSubject;
179 import edu.internet2.middleware.grouper.subj.LazySubject;
180 import edu.internet2.middleware.grouper.subj.SubjectHelper;
181 import edu.internet2.middleware.grouper.tableIndex.TableIndex;
182 import edu.internet2.middleware.grouper.tableIndex.TableIndexType;
183 import edu.internet2.middleware.grouper.util.GrouperUtil;
184 import edu.internet2.middleware.grouper.util.PerformanceLogger;
185 import edu.internet2.middleware.grouper.validator.AddAlternateGroupNameValidator;
186 import edu.internet2.middleware.grouper.validator.AddCompositeMemberValidator;
187 import edu.internet2.middleware.grouper.validator.CanOptinValidator;
188 import edu.internet2.middleware.grouper.validator.CanOptoutValidator;
189 import edu.internet2.middleware.grouper.validator.CompositeValidator;
190 import edu.internet2.middleware.grouper.validator.FieldTypeValidator;
191 import edu.internet2.middleware.grouper.validator.GrouperValidator;
192 import edu.internet2.middleware.grouper.validator.NamingValidator;
193 import edu.internet2.middleware.grouper.validator.NotNullOrEmptyValidator;
194 import edu.internet2.middleware.grouper.validator.NotNullValidator;
195 import edu.internet2.middleware.grouper.xml.export.XmlExportGroup;
196 import edu.internet2.middleware.grouper.xml.export.XmlImportable;
197 import edu.internet2.middleware.grouperClient.collections.MultiKey;
198 import edu.internet2.middleware.subject.Source;
199 import edu.internet2.middleware.subject.SourceUnavailableException;
200 import edu.internet2.middleware.subject.Subject;
201 import edu.internet2.middleware.subject.SubjectNotFoundException;
202 import edu.internet2.middleware.subject.SubjectNotUniqueException;
203 
204 
205 /** 
206  * A group within the Groups Registry.
207  * <p/>
208  * @author  blair christensen.
209  * @version $Id: Group.java,v 1.269 2009-12-15 06:47:06 mchyzer Exp $
210  */
211 @SuppressWarnings("serial")
212 public class Group extends GrouperAPI implements Role, GrouperHasContext, Owner, 
213     Hib3GrouperVersioned, Comparable, XmlImportable<Group>, AttributeAssignable, Entity, GrouperObject {
214 
215   /**
216    * 
217    */
218   public static final String VALIDATION_GROUP_DESCRIPTION_TOO_LONG_KEY = "groupDescriptionTooLong";
219 
220   /**
221    * 
222    */
223   public static final String VALIDATION_GROUP_DISPLAY_EXTENSION_TOO_LONG_KEY = "groupDisplayExtensionTooLong";
224 
225   /**
226    * 
227    */
228   public static final String VALIDATION_GROUP_EXTENSION_TOO_LONG_KEY = "groupExtensionTooLong";
229 
230   /**
231    * 
232    */
233   public static final String VALIDATION_GROUP_DISPLAY_NAME_TOO_LONG_KEY = "groupDisplayNameTooLong";
234 
235   /**
236    * 
237    */
238   public static final String VALIDATION_GROUP_NAME_TOO_LONG_KEY = "groupNameTooLong";
239 
240   /** name of the groups table in the db */
241   public static final String TABLE_GROUPER_GROUPS = "grouper_groups";
242   
243   /** uuid col in db (not used anymore) */
244   public static final String COLUMN_UUID = "uuid";
245   
246   /** id col in db */
247   public static final String COLUMN_ID = "id";
248   
249   /** col in db */
250   public static final String COLUMN_PARENT_STEM = "parent_stem";
251   
252   /** col in db */
253   public static final String COLUMN_CREATOR_ID = "creator_id";
254 
255   /** col in db */
256   public static final String COLUMN_CREATE_TIME = "create_time";
257 
258   /** col in db */
259   public static final String COLUMN_MODIFIER_ID = "modifier_id";
260 
261   /** col in db */
262   public static final String COLUMN_MODIFY_TIME = "modify_time";
263   
264   /** col in db */
265   public static final String COLUMN_NAME = "name";
266   
267   /** col in db */
268   public static final String COLUMN_DISPLAY_NAME = "display_name";
269   
270   /** col in db */
271   public static final String COLUMN_EXTENSION = "extension";
272 
273   /** col in db */
274   public static final String COLUMN_DISPLAY_EXTENSION = "display_extension";
275 
276   /** col in db */
277   public static final String COLUMN_DESCRIPTION = "description";
278   
279   /** old id col for id conversion */
280   public static final String COLUMN_OLD_ID = "old_id";
281   
282   /** old uuid id col for id conversion */
283   public static final String COLUMN_OLD_UUID = "old_uuid";
284   
285   /** timestamp of the last membership change for this group */
286   public static final String COLUMN_LAST_MEMBERSHIP_CHANGE = "last_membership_change";
287   
288   /** timestamp of the last immediate membership change for this group */
289   public static final String COLUMN_LAST_IMMEDIATE_MEMBERSHIP_CHANGE = "last_imm_membership_change";
290   
291   /** an alternate name for this group */
292   public static final String COLUMN_ALTERNATE_NAME = "alternate_name";
293   
294   /** if this is a group or role */
295   public static final String COLUMN_TYPE_OF_GROUP = "type_of_group";
296   
297   /** unique number for this group */
298   public static final String COLUMN_ID_INDEX = "id_index";
299   
300   /** epoch time of when to disable membership */
301   public static final String COLUMN_DISABLED_TIMESTAMP = "disabled_timestamp";
302   
303   /** epoch time of when to enable membership */
304   public static final String COLUMN_ENABLED_TIMESTAMP = "enabled_timestamp";
305   
306   /** whether the membership is enabled or disabled: T|F */
307   public static final String COLUMN_ENABLED = "enabled";
308   
309   /**
310    * if this is a composite group, get the composite object for this group
311    * @return the composite group
312    * @throws CompositeNotFoundException if composite not found
313    * @deprecated use the overload with boolean instead
314    */
315   @Deprecated
316   public Composite getComposite() throws CompositeNotFoundException {
317     return this.getComposite(true);
318   }
319   
320   /**
321    * if this is a composite group, get the composite object for this group
322    * @param throwExceptionIfNotFound 
323    * @return the composite group or null if none
324    * @throws CompositeNotFoundException if not found and throwExceptionIfNotFound is true
325    */
326   public Composite getComposite(boolean throwExceptionIfNotFound) {
327     try {
328       return CompositeFinder.findAsOwner(this, true);
329     } catch (CompositeNotFoundException cnfe) {
330       if (throwExceptionIfNotFound) {
331         throw cnfe;
332       }
333       return null;
334     }
335   }
336   
337   /**
338    * <pre>
339    * create or update a group.  Note this will not rename a group at this time (might in future)
340    * 
341    * This is a static method since setters to Group objects persist to the DB
342    * 
343    * Steps:
344    * 
345    * 1. Find the group by groupNameToEdit
346    * 2. Internally set all the fields of the stem (no need to reset if already the same)
347    * 3. Store the group (insert or update) if needed
348    * 4. Return the group object
349    * 
350    * This runs in a tx so that if part of it fails the whole thing fails, and potentially the outer
351    * transaction too
352    * </pre>
353    * @param GROUPER_SESSION to act as
354    * @param groupNameToEdit is the name of the group to edit (or null if insert)
355    * @param description new description for group
356    * @param displayExtension display friendly name for this group only
357    * (parent stems are not specified)
358    * @param name this is required, and is the full name of the group
359    * including the names of parent stems.  e.g. stem1:stem2:stem3
360    * the parent stem must exist unless createParentStemsIfNotExist.  
361    * Can rename a stem extension, but not the parent stem name (move)
362    * @param uuid of the group.  If a group exists with this uuid, then it will
363    * be updated, if not, then it will be created if createIfNotExist is true
364    * @param saveMode to constrain if insert only or update only, if null defaults to INSERT_OR_UPDATE
365    * @param createParentStemsIfNotExist true if the stems should be created if they dont exist, false
366    * for StemNotFoundException if not exist.  Note, the display extension on created stems
367    * will equal the extension
368    * @return the stem that was updated or created
369    * @throws StemNotFoundException 
370    * @throws InsufficientPrivilegeException 
371    * @throws StemAddException 
372    * @throws GroupModifyException 
373    * @throws GroupNotFoundException
374    * @throws GroupAddException
375    */
376   public static Group saveGroup(final GrouperSession GROUPER_SESSION, final String groupNameToEdit,
377       final String uuid, final String name, final String displayExtension, final String description, 
378       SaveMode saveMode, final boolean createParentStemsIfNotExist) 
379         throws StemNotFoundException, InsufficientPrivilegeException, StemAddException, 
380         GroupModifyException, GroupNotFoundException, GroupAddException {
381 
382     GroupSaveroupSave.html#GroupSave">GroupSave groupSave = new GroupSave(GROUPER_SESSION);
383 
384     groupSave.assignGroupNameToEdit(groupNameToEdit).assignUuid(uuid);
385     groupSave.assignName(name).assignDisplayExtension(displayExtension);
386     groupSave.assignDescription(description).assignSaveMode(saveMode);
387     groupSave.assignCreateParentStemsIfNotExist(createParentStemsIfNotExist);
388     Group group = groupSave.save();
389 
390     return group;
391 
392   }
393   
394   /** */
395   private static final  EventLogEventLog                  EVENT_LOG            = new EventLog();
396   /** */
397   private static final  String                    KEY_CREATOR   = "creator";  // for state caching 
398   /** */
399   private static final  String                    KEY_MODIFIER  = "modifier"; // for state caching
400   /** */
401   private static final  String                    KEY_SUBJECT   = "subject";  // for state caching
402   
403   /** */
404   @GrouperIgnoreDbVersion 
405   @GrouperIgnoreFieldConstant
406   @GrouperIgnoreClone
407   private Member cachedMember  = null;
408 
409   /** */
410   @GrouperIgnoreDbVersion 
411   @GrouperIgnoreFieldConstant
412   @GrouperIgnoreClone
413   private               HashMap<String, Subject>  subjectCache  = new HashMap<String, Subject>();
414   // TODO 20070531 review lazy-loading to improve consistency + performance
415 
416   /** */
417   @GrouperIgnoreDbVersion 
418   @GrouperIgnoreFieldConstant
419   @GrouperIgnoreClone
420   // caching legacy attributes with the group object like they were cached < 2.2
421   // the key is the legacy attribute name.
422   private Map<String, AttributeAssignValue>       attributes;
423   /** */
424   private long      createTime      = 0; // default to the epoch
425   /** */
426   private String    creatorUUID;
427   
428   /** */
429   private String    modifierUUID;
430   /** */
431   private long      modifyTime      = 0; // default to the epoch
432   /** */
433   private String    parentUuid;
434 
435   /** default to group type, as opposed to role */
436   private TypeOfGroup typeOfGroup = TypeOfGroup.group;
437   
438   /** caching legacy group types with the group object like they were cached < 2.2
439    * keys are the legacy group type names (i.e. without prefix or path) */
440   @GrouperIgnoreDbVersion 
441   @GrouperIgnoreFieldConstant
442   @GrouperIgnoreClone
443   private Map<String, AttributeDefName> types;
444   
445   /** cache type assignments as well for the same reason */
446   private Map<String, AttributeAssign> typeAssignments;
447   
448   /** */
449   private String    uuid;
450   
451   /** name of group, e.g. school:community:students */
452   private String name;
453   
454   /** alternate name of group */
455   private String alternateNameDb;
456   
457   /** displayName of group, e.g. My School:Community Groups:All Students */
458   private String displayName;
459   
460   /** extension of group, e.g. students */
461   private String extension;
462   
463   /** id of the group as a unique integer */
464   private Long idIndex;
465   
466   /** displayExtension of group, e.g. All Students */
467   private String displayExtension;
468   
469   /** description of group, friendly description, e.g. in sentence form, about what the group is about */
470   private String description;
471   
472   /** context id of the transaction */
473   private String contextId;
474   
475   /**
476    * If the group is enabled.
477    */
478   private boolean enabled = true;
479   
480   /**
481    * Time to enable this group.
482    */
483   private Long enabledTimeDb;
484   
485   /**
486    * Time to disable this group.
487    */
488   private Long disabledTimeDb;
489 
490   /**
491    * context id of the transaction
492    * @return context id
493    */
494   public String getContextId() {
495     return this.contextId;
496   }
497 
498   /**
499    * context id of the transaction
500    * @param contextId1
501    */
502   public void setContextId(String contextId1) {
503     this.contextId = contextId1;
504   }
505 
506 
507   
508   //*****  START GENERATED WITH GenerateFieldConstants.java *****//
509 
510   /** constant for field name for: alternateNameDb */
511   public static final String FIELD_ALTERNATE_NAME_DB = "alternateNameDb";
512   
513   /** constant for field name for: createTime */
514   public static final String FIELD_CREATE_TIME = "createTime";
515 
516   /** constant for field name for: creatorUUID */
517   public static final String FIELD_CREATOR_UUID = "creatorUUID";
518 
519   /** constant for field name for: dbVersion */
520   public static final String FIELD_DB_VERSION = "dbVersion";
521 
522   /** constant for field name for: description */
523   public static final String FIELD_DESCRIPTION = "description";
524 
525   /** constant for field name for: displayExtension */
526   public static final String FIELD_DISPLAY_EXTENSION = "displayExtension";
527 
528   /** constant for field name for: displayName */
529   public static final String FIELD_DISPLAY_NAME = "displayName";
530 
531   /** constant for field name for: extension */
532   public static final String FIELD_EXTENSION = "extension";
533   
534   /** constant for field name for: idIndex */
535   public static final String FIELD_ID_INDEX = "idIndex";
536   
537   /** constant for field name for: lastMembershipChangeDb */
538   public static final String FIELD_LAST_MEMBERSHIP_CHANGE_DB = "lastMembershipChangeDb";
539 
540   /** constant for field name for: lastImmediateMembershipChangeDb */
541   public static final String FIELD_LAST_IMMEDIATE_MEMBERSHIP_CHANGE_DB = "lastImmediateMembershipChangeDb";
542   
543   /** constant for field name for: modifierUUID */
544   public static final String FIELD_MODIFIER_UUID = "modifierUUID";
545 
546   /** constant for field name for: modifyTime */
547   public static final String FIELD_MODIFY_TIME = "modifyTime";
548 
549   /** constant for field name for: name */
550   public static final String FIELD_NAME = "name";
551 
552   /** constant for field name for: parentUuid */
553   public static final String FIELD_PARENT_UUID = "parentUuid";
554 
555   /** constant for field name for: typeOfGroup */
556   public static final String FIELD_TYPE_OF_GROUP = "typeOfGroup";
557 
558   /** constant for field name for: uuid */
559   public static final String FIELD_UUID = "uuid";
560   
561   /** constant for field name for: disabledTimeDb */
562   public static final String FIELD_DISABLED_TIME_DB = "disabledTimeDb";
563 
564   /** constant for field name for: enabled */
565   public static final String FIELD_ENABLED = "enabled";
566 
567   /** constant for field name for: enabledTimeDb */
568   public static final String FIELD_ENABLED_TIME_DB = "enabledTimeDb";
569 
570   /**
571    * fields which are included in db version
572    */
573   private static final Set<String> DB_VERSION_FIELDS = GrouperUtil.toSet(
574       FIELD_CREATE_TIME, FIELD_CREATOR_UUID, FIELD_DESCRIPTION, 
575       FIELD_DISPLAY_EXTENSION, FIELD_DISPLAY_NAME, FIELD_EXTENSION, FIELD_MODIFIER_UUID, 
576       FIELD_MODIFY_TIME, FIELD_NAME, FIELD_PARENT_UUID, FIELD_TYPE_OF_GROUP, FIELD_UUID, 
577       FIELD_ID_INDEX, FIELD_DISABLED_TIME_DB, FIELD_ENABLED, FIELD_ENABLED_TIME_DB,
578       FIELD_ALTERNATE_NAME_DB, FIELD_LAST_MEMBERSHIP_CHANGE_DB, FIELD_LAST_IMMEDIATE_MEMBERSHIP_CHANGE_DB);
579 
580   /**
581    * fields which are included in clone method
582    */
583   private static final Set<String> CLONE_FIELDS = GrouperUtil.toSet(
584       FIELD_CREATE_TIME, FIELD_CREATOR_UUID, FIELD_DB_VERSION, 
585       FIELD_DESCRIPTION, FIELD_DISPLAY_EXTENSION, FIELD_DISPLAY_NAME, FIELD_EXTENSION, 
586       FIELD_HIBERNATE_VERSION_NUMBER, FIELD_ID_INDEX, FIELD_MODIFIER_UUID, FIELD_MODIFY_TIME, FIELD_NAME, 
587       FIELD_PARENT_UUID, FIELD_TYPE_OF_GROUP, FIELD_UUID, FIELD_LAST_MEMBERSHIP_CHANGE_DB, 
588       FIELD_ALTERNATE_NAME_DB, FIELD_LAST_IMMEDIATE_MEMBERSHIP_CHANGE_DB,
589       FIELD_DISABLED_TIME_DB, FIELD_ENABLED, FIELD_ENABLED_TIME_DB);
590 
591   //*****  END GENERATED WITH GenerateFieldConstants.java *****//
592 
593 
594   /**
595    * Should this group be enabled based on the enabled and disabled dates?
596    * @return boolean
597    */
598   private boolean internal_isEnabledUsingTimestamps() {
599     long now = System.currentTimeMillis();
600     if (this.enabledTimeDb != null && this.enabledTimeDb > now) {
601       return false;
602     }
603     if (this.disabledTimeDb != null && this.disabledTimeDb < now) {
604       return false;
605     }
606     return true;
607   }
608   
609   /**
610    * Is this group enabled?
611    * @return boolean
612    */
613   public boolean isEnabled() {
614     return this.enabled;
615   }
616   
617   /**
618    * Whether to enable or disable this group.
619    * @param enabled
620    */
621   public void setEnabled(boolean enabled) {
622     this.enabled = enabled;
623   }
624 
625   /**
626    * Whether or not this group is enabled.
627    * @return the enabled
628    */
629   public String getEnabledDb() {
630     if (this.enabled) {
631       return "T";
632     }
633     
634     return "F";
635   }
636 
637   
638   /**
639    * Whether to enable or disable this group.
640    * @param enabled
641    */
642   public void setEnabledDb(String enabled) {
643     this.enabled = GrouperUtil.booleanValue(enabled);
644   }
645 
646   /**
647    * Time when this group is enabled if the value is in the future.
648    * @return Long
649    */
650   public Long getEnabledTimeDb() {
651     return this.enabledTimeDb;
652   }
653 
654   /**
655    * Time when this group is enabled if the value is in the future.
656    * @return Timestamp
657    */
658   public Timestamp getEnabledTime() {
659     if (this.enabledTimeDb == null) {
660       return null;
661     }
662     
663     return new Timestamp(this.enabledTimeDb);
664   }
665   
666   /**
667    * Set the time when this group should be enabled.
668    * @param enabledTimeDb
669    */
670   public void setEnabledTimeDb(Long enabledTimeDb) {
671     this.enabledTimeDb = enabledTimeDb;
672   }
673 
674   /**
675    * Set the time when this group should be enabled.
676    * @param enabledTimeDb
677    */
678   public void setEnabledTime(Timestamp enabledTimeDb) {
679     if (enabledTimeDb == null) {
680       this.enabledTimeDb = null;
681     } else {
682       this.enabledTimeDb = enabledTimeDb.getTime();
683     }
684     
685     setEnabled(internal_isEnabledUsingTimestamps());
686   }
687   
688   /**
689    * Time when this group is disabled if the value is in the future.
690    * @return Long
691    */
692   public Long getDisabledTimeDb() {
693     return this.disabledTimeDb;
694   }
695 
696   /**
697    * Time when this group is disabled if the value is in the future.
698    * @return Timestamp
699    */
700   public Timestamp getDisabledTime() {
701     if (this.disabledTimeDb == null) {
702       return null;
703     }
704     
705     return new Timestamp(this.disabledTimeDb);
706   }
707   
708   /**
709    * Set the time to disable this group.
710    * @param disabledTimeDb
711    */
712   public void setDisabledTimeDb(Long disabledTimeDb) {
713     this.disabledTimeDb = disabledTimeDb;
714   }
715   
716   /**
717    * Set the time to disable this group.
718    * @param disabledTimeDb
719    */
720   public void setDisabledTime(Timestamp disabledTimeDb) {
721     if (disabledTimeDb == null) {
722       this.disabledTimeDb = null;
723     } else {
724       this.disabledTimeDb = disabledTimeDb.getTime();
725     }
726     
727     setEnabled(this.internal_isEnabledUsingTimestamps());
728   }
729   
730   /** */
731   @GrouperIgnoreClone @GrouperIgnoreDbVersion @GrouperIgnoreFieldConstant
732   private AttributeAssignGroupDelegate attributeAssignGroupDelegate;
733   
734   /**
735    * 
736    * @return the delegate
737    */
738   public AttributeAssignGroupDelegate getAttributeDelegate() {
739     if (this.attributeAssignGroupDelegate == null) {
740       this.attributeAssignGroupDelegate = new AttributeAssignGroupDelegate(this);
741     }
742     return this.attributeAssignGroupDelegate;
743   }
744   
745   /** */
746   @GrouperIgnoreClone @GrouperIgnoreDbVersion @GrouperIgnoreFieldConstant
747   private AttributeValueDelegate attributeValueDelegate;
748   
749   /**
750    * this delegate works on attributes and values at the same time
751    * @return the delegate
752    */
753   public AttributeValueDelegate getAttributeValueDelegate() {
754     if (this.attributeValueDelegate == null) {
755       this.attributeValueDelegate = new AttributeValueDelegate(this.getAttributeDelegate());
756     }
757     return this.attributeValueDelegate;
758   }
759   
760   /**
761    * delegate for effective memberships
762    * @param member 
763    * @return the delegate
764    */
765   public AttributeAssignEffMshipDelegate getAttributeDelegateEffMship(Member member) {
766     return new AttributeAssignEffMshipDelegate(this, member);
767   }
768   
769   /**
770    * this delegate works on attributes and values at the same time
771    * @param member 
772    * @return the delegate
773    */
774   public AttributeValueDelegate getAttributeValueDelegateEffMship(Member member) {
775     return new AttributeValueDelegate(this.getAttributeDelegateEffMship(member));
776   }
777   
778 
779   /**
780    * delegate for effective memberships
781    * @param member 
782    * @return the delegate
783    */
784   public AttributeAssignMembershipDelegate getAttributeDelegateMembership(Member member) {
785     return getAttributeDelegateMembership(member, Group.getDefaultList());
786   }
787   
788   /**
789    * delegate for effective memberships
790    * @param member 
791    * @param field 
792    * @return the delegate
793    */
794   public AttributeAssignMembershipDelegate getAttributeDelegateMembership(Member member, Field field) {
795     Membership membership = MembershipFinder.findImmediateMembership(GrouperSession.staticGrouperSession(), 
796         this, member.getSubject(), field, false);
797     if (membership == null) {
798       throw new RuntimeException("Cannot get the immediate membership attribute delegate if not an immediate member: " 
799           + this + ", " + GrouperUtil.subjectToString(member.getSubject()) + ", " + field);
800     }
801     return new AttributeAssignMembershipDelegate(membership);
802   }
803   
804   /**
805    * this delegate works on attributes and values at the same time
806    * @param member 
807    * @param field 
808    * @return the delegate
809    */
810   public AttributeValueDelegate getAttributeValueDelegateMembership(Member member, Field field) {
811     return new AttributeValueDelegate(this.getAttributeDelegateMembership(member, field));
812   }
813 
814   /**
815    * this delegate works on attributes and values at the same time
816    * @param member 
817    * @return the delegate
818    */
819   public AttributeValueDelegate getAttributeValueDelegateMembership(Member member) {
820     return new AttributeValueDelegate(this.getAttributeDelegateMembership(member));
821   }
822 
823 
824   /**
825    * Retrieve default members {@link Field}.
826    * <pre class="eg">
827    * Field members = Group.getDefaultList();
828    * </pre>
829    * @return  The "members" {@link Field}
830    * @throws  GrouperException
831    */
832   public static Field getDefaultList() throws  GrouperException {
833     return FieldFinder.find(GrouperConfig.LIST, true);
834   }
835 
836   /** logger */
837   private static final Log LOG = GrouperUtil.getLog(Group.class);
838 
839   /**
840    * Add ore replace a composite membership to this group.
841    * <pre class="eg">
842    * try {
843    *   g.assignCompositeMember(CompositeType.UNION, leftGroup, rightGroup);
844    * }
845    * catch (InsufficientPrivilegeException eIP) {
846    *   // Not privileged to add members 
847    * }
848    * catch (MemberAddException eMA) {
849    *   // Unable to add composite membership
850    * } 
851    * </pre>
852    * @param   type  Add membership of this {@link CompositeType}.
853    * @param   left  {@link Group} that is left factor of of composite membership.
854    * @param   right {@link Group} that is right factor of composite membership.
855    * @throws  InsufficientPrivilegeException
856    * @throws  MemberAddException
857    * @throws MemberDeleteException 
858    * @since   1.0
859    */
860   public void assignCompositeMember(final CompositeType type, final Grouprouper/Group.html#Group">Group left, final Group right)
861     throws  InsufficientPrivilegeException, MemberAddException, MemberDeleteException {
862 
863     final String errorMessageSuffix = ", group name: " + this.name + ", compositeType: " + type
864       + ", left group name: " + (left == null ? "null" : left.getName()) 
865       + ", right group name: " + (right == null ? "null" : right.getName());
866 
867     Composite composite = null;
868     try {
869       composite = this.getComposite();
870     } catch (CompositeNotFoundException cnfe) {
871       
872     }
873     if (composite != null) {
874       
875       //if not equal, replace, otherwise leave alone
876       if (!composite.getTypeDb().equals(type.getName())
877           || !composite.getLeftFactorUuid().equals(left.getUuid())
878           || !composite.getRightFactorUuid().equals(right.getUuid())) {
879         
880         if (LOG.isDebugEnabled()) {
881           LOG.debug("Deleting and adding composite member for group: " + this.getExtension() + ": " 
882               + type.getName() + ": " + left.getExtension() + " - " + right.getExtension());
883         }
884         
885         final StringBuilder differences = new StringBuilder();
886         if (!composite.getTypeDb().equals(type.getName())) {
887           differences.append("type from: " + composite.getTypeDb() + " to: " + type.getName());
888         }
889         if (!composite.getLeftFactorUuid().equals(left.getUuid())) {
890           try {
891             differences.append("left group from: " + composite.getLeftGroup().getName() + ", to: " + left.getName());
892           } catch (GroupNotFoundException gnfe) {
893             differences.append("left group from: " + composite.getLeftFactorUuid() + ", to: " + left.getName());
894           }
895         }
896         if (!composite.getRightFactorUuid().equals(right.getUuid())) {
897           try {
898             differences.append("right group from: " + composite.getRightGroup().getName() + ", to: " + right.getName());
899           } catch (GroupNotFoundException gnfe) {
900             differences.append("right group from: " + composite.getRightFactorUuid() + ", to: " + right.getName());
901           }
902         }
903         final Composite COMPOSITE = composite;
904 
905         HibernateSession.callbackHibernateSession(
906             GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
907             new HibernateHandler() {
908 
909               public Object callback(HibernateHandlerBean hibernateHandlerBean)
910                   throws GrouperDAOException {
911                 
912                 try {
913 
914                   hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
915 
916                   Group.this.deleteCompositeMember();
917                   Group.this.addCompositeMember(type, left, right);
918                   
919                   if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
920                     
921                     AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_COMPOSITE_UPDATE, "id", 
922                         COMPOSITE.getUuid(), "ownerId", Group.this.getUuid(), "ownerName", Group.this.getName(), "leftFactorId", 
923                         left.getUuid(), "leftFactorName", left.getName(), "rightFactorId", right.getUuid(), 
924                         "rightFactorName", right.getName(), "type", type.toString());
925                     auditEntry.setDescription("Updated composite: " + Group.this.getName() + ", " + differences.toString());
926                     auditEntry.saveOrUpdate(true);
927                   }
928 
929                   return null;
930                 } catch (MemberAddException mae) {
931                   GrouperUtil.injectInException(mae, errorMessageSuffix);
932                   throw mae;
933                 } catch (RuntimeException re) {
934                   GrouperUtil.injectInException(re, errorMessageSuffix);
935                   throw re;
936                 }
937               }
938             });
939       }
940     } else {
941       if (LOG.isDebugEnabled()) {
942         LOG.debug("Adding composite member for group: " + this.getExtension() + ": " 
943             + type.getName() + ": " + left.getExtension() + " - " + right.getExtension());
944       }
945       this.addCompositeMember(type, left, right);
946     }
947     
948   }
949   
950   /**
951    * Add a composite membership to this group.
952    * <pre class="eg">
953    * try {
954    *   g.addCompositeMember(CompositeType.UNION, leftGroup, rightGroup);
955    * }
956    * catch (InsufficientPrivilegeException eIP) {
957    *   // Not privileged to add members 
958    * }
959    * catch (MemberAddException eMA) {
960    *   // Unable to add composite membership
961    * } 
962    * </pre>
963    * @param   type  Add membership of this {@link CompositeType}.
964    * @param   left  {@link Group} that is left factor of of composite membership.
965    * @param   right {@link Group} that is right factor of composite membership.
966    * @return composite
967    * @throws  InsufficientPrivilegeException
968    * @throws  MemberAddException
969    * @since   1.0
970    */
971   public Composite addCompositeMember(CompositeType type, Groupref="../../../../edu/internet2/middleware/grouper/Group.html#Group">Group left, Group right)
972     throws  InsufficientPrivilegeException,
973             MemberAddException {
974     return internal_addCompositeMember(GrouperSession.staticGrouperSession(), type, left, right, null);
975   } 
976 
977   /**
978    * Add a subject to this group as immediate member.
979    * 
980    * An immediate member is directly assigned to a group.
981    * A composite group has no immediate members.  Note that a 
982    * member can have 0 to 1 immediate memberships
983    * to a single group, and 0 to many effective memberships to a group.
984    * A group can have potentially unlimited effective 
985    * memberships
986    * 
987    * <pre class="eg">
988    * try {
989    *   g.addMember(subj);
990    * }
991    * catch (InsufficientPrivilegeException eIP) {
992    *   // Not privileged to add members 
993    * }
994    * catch (MemberAddException eMA) {
995    *   // Unable to add subject
996    * } 
997    * </pre>
998    * @param   subj  Add this {@link Subject}
999    * @throws  InsufficientPrivilegeException
1000    * @throws  MemberAddException
1001    */
1002   public void addMember(Subject subj) 
1003     throws  InsufficientPrivilegeException,
1004             MemberAddException
1005   {
1006     this.addMember(subj, true);
1007   } // public void addMember(subj)
1008 
1009   /**
1010    * Add a subject to this group as immediate member.
1011    * 
1012    * An immediate member is directly assigned to a group.
1013    * A composite group has no immediate members.  Note that a 
1014    * member can have 0 to 1 immediate memberships
1015    * to a single group, and 0 to many effective memberships to a group.
1016    * A group can have potentially unlimited effective 
1017    * memberships
1018    * 
1019    * <pre class="eg">
1020    * try {
1021    *   g.addMember(subj);
1022    * }
1023    * catch (InsufficientPrivilegeException eIP) {
1024    *   // Not privileged to add members 
1025    * }
1026    * catch (MemberAddException eMA) {
1027    *   // Unable to add subject
1028    * } 
1029    * </pre>
1030    * @param   subj  Add this {@link Subject}
1031    * @param exceptionIfAlreadyMember if false, and subject is already a member,
1032    * then dont throw a MemberAddException if the member is already in the group
1033    * @return false if it already existed, true if it didnt already exist
1034    * @throws  InsufficientPrivilegeException
1035    * @throws  MemberAddException
1036    */
1037   public boolean addMember(Subject subj, boolean exceptionIfAlreadyMember) 
1038     throws  InsufficientPrivilegeException,
1039             MemberAddException
1040   {
1041     try {
1042       Field defaultList = getDefaultList();
1043       return this.addMember(subj, defaultList, exceptionIfAlreadyMember);
1044     }
1045     catch (SchemaException eS) {
1046       throw new MemberAddException(eS.getMessage(), eS);
1047     }
1048   } // public void addMember(subj)
1049 
1050   /**
1051    * Add a subject to this group as immediate member.
1052    * 
1053    * An immediate member is directly assigned to a group.
1054    * A composite group has no immediate members.  Note that a 
1055    * member can have 0 to 1 immediate memberships
1056    * to a single group, and 0 to many effective memberships to a group.
1057    * A group can have potentially unlimited effective 
1058    * memberships
1059    * 
1060    * <pre class="eg">
1061    * try {
1062    *   g.addMember(subj, f);
1063    * }
1064    * catch (InsufficientPrivilegeException eIP) {
1065    *   // Not privileged to add members 
1066    * }
1067    * catch (MemberAddException eMA) {
1068    *   // Unable to add member
1069    * } 
1070    * catch (SchemaException eS) {
1071    *   // Invalid Field
1072    * } 
1073    * </pre>
1074    * @param   subj  Add this {@link Subject}
1075    * @param   f     Add subject to this {@link Field}.
1076    * @throws  InsufficientPrivilegeException
1077    * @throws  MemberAddException
1078    * @throws  SchemaException
1079    */
1080   public void addMember(Subject subj, Field f)
1081     throws  InsufficientPrivilegeException,
1082             MemberAddException,
1083             SchemaException
1084   {
1085     this.addMember(subj, f, true);
1086   } // public void addMember(subj, f)
1087 
1088   /**
1089    * Add a subject to this group as immediate member.
1090    * 
1091    * An immediate member is directly assigned to a group.
1092    * A composite group has no immediate members.  Note that a 
1093    * member can have 0 to 1 immediate memberships
1094    * to a single group, and 0 to many effective memberships to a group.
1095    * A group can have potentially unlimited effective 
1096    * memberships
1097    * 
1098    * <pre class="eg">
1099    * try {
1100    *   g.addMember(subj, f);
1101    * }
1102    * catch (InsufficientPrivilegeException eIP) {
1103    *   // Not privileged to add members 
1104    * }
1105    * catch (MemberAddException eMA) {
1106    *   // Unable to add member
1107    * } 
1108    * catch (SchemaException eS) {
1109    *   // Invalid Field
1110    * } 
1111    * </pre>
1112    * @param   subj  Add this {@link Subject}
1113    * @param   f     Add subject to this {@link Field}.
1114    * @param exceptionIfAlreadyMember if false, and subject is already a member,
1115    * then dont throw a MemberAddException if the member is already in the group
1116    * @throws  InsufficientPrivilegeException
1117    * @throws  MemberAddException
1118    * @throws  SchemaException
1119    * @return false if it already existed, true if it didnt already exist
1120    */
1121   public boolean addMember(final Subject subj, final Field f, final boolean exceptionIfAlreadyMember)
1122     throws  InsufficientPrivilegeException,
1123             MemberAddException, SchemaException {
1124     return this.internal_addMember(subj, f, exceptionIfAlreadyMember, null, null, null);
1125   }
1126 
1127   /**
1128    * replace the member list with new list.  Note this is not done in a tx so it can make as much progress as possible
1129    * though feel free to put a tx outside of the call if you need it
1130    * @param newSubjectList
1131    * @return the number of members changed
1132    */
1133   public int replaceMembers(Collection<Subject> newSubjectList) {
1134     return replaceMembers(newSubjectList, Group.getDefaultList());
1135   }
1136 
1137   /**
1138    * replace the member list with new list.  Note this is not done in a tx so it can make as much progress as possible
1139    * though feel free to put a tx outside of the call if you need it
1140    * @param newSubjectList
1141    * @param field
1142    * @return the number of members changed
1143    */
1144   public int replaceMembers(Collection<Subject> newSubjectList, Field field) {
1145     
1146     Map<String, Object> infoMap = new HashMap<String, Object>();
1147     infoMap.put("operation", "replaceMembers");
1148     infoMap.put("groupName", this.getName());
1149     
1150     try {
1151       Set<Member> existingMembers = GrouperUtil.nonNull(this.getMembers(field));
1152       
1153       infoMap.put("existingMemberListSize", GrouperUtil.length(existingMembers));
1154       infoMap.put("newMemberListSize", GrouperUtil.length(newSubjectList));
1155       
1156       //multikey is the sourceId to the subjectId
1157       Map<MultiKey, Member> existingMemberMap = new HashMap<MultiKey, Member>();
1158       
1159       int changedRecords = 0;
1160       
1161       //register all the existing subjects
1162       for (Member member : existingMembers) {
1163         
1164         MultiKey multiKey = new MultiKey(member.getSubjectSourceId(), member.getSubjectId());
1165         
1166         existingMemberMap.put(multiKey, member);
1167         
1168       }
1169   
1170       //lets clone this since we will tear it down
1171       Map<MultiKey, Subject> newMemberMap = new HashMap<MultiKey, Subject>();
1172       
1173       //lets see which ones are there already
1174       for (Subject subject : GrouperUtil.nonNull(newSubjectList)) {
1175         
1176         MultiKey multiKey = new MultiKey(subject.getSourceId(), subject.getId());
1177         
1178         newMemberMap.put(multiKey, subject);
1179         
1180       }
1181       
1182       //lets remove from both the ones from both lists that already exist
1183       Iterator<MultiKey> newMemberListIterator = newMemberMap.keySet().iterator();
1184       
1185       while (newMemberListIterator.hasNext()) {
1186         
1187         MultiKey newMemberMultiKey = newMemberListIterator.next();
1188         
1189         if (existingMemberMap.containsKey(newMemberMultiKey)) {
1190           newMemberListIterator.remove();
1191           existingMemberMap.remove(newMemberMultiKey);
1192           if (LOG.isDebugEnabled()) {
1193             LOG.debug("Replace members on " + this.getName() + ": Subject " 
1194                 + newMemberMultiKey.getKey(0) + " - " + newMemberMultiKey.getKey(1) + " already is in the list");
1195           }
1196         }
1197       }
1198   
1199       int addedMemberCount = 0;
1200       
1201       //now the new list is the adds, and the existing list is the removes...
1202       //lets do that adds
1203       for (MultiKey newMemberMultiKey : newMemberMap.keySet()) {
1204         
1205         Subject newSubject = newMemberMap.get(newMemberMultiKey);
1206         
1207         this.addMember(newSubject, field, true);
1208         
1209         if (LOG.isDebugEnabled()) {
1210           LOG.debug("Replace members on " + this.getName() + ": Subject " 
1211               + newMemberMultiKey.getKey(0) + " - " + newMemberMultiKey.getKey(1) + " is added");
1212         }
1213   
1214         changedRecords++;
1215         addedMemberCount++;
1216       }
1217 
1218       infoMap.put("addedMemberCount", addedMemberCount);
1219       
1220       int deletedMemberCount = 0;
1221       //now lets do the removes
1222       for (MultiKey removeMultiKey : existingMemberMap.keySet()) {
1223         
1224         Member removeMember = existingMemberMap.get(removeMultiKey);
1225         
1226         this.deleteMember(removeMember, field, true);
1227 
1228         if (LOG.isDebugEnabled()) {
1229           LOG.debug("Replace members on " + this.getName() + ": Subject " 
1230               + removeMultiKey.getKey(0) + " - " + removeMultiKey.getKey(1) + " already is removed");
1231         }
1232 
1233         changedRecords++;
1234         deletedMemberCount++;
1235       }
1236       
1237       infoMap.put("deletedMemberCount", deletedMemberCount);
1238       infoMap.put("changedRecords", changedRecords);
1239       
1240       if (LOG.isInfoEnabled()) {
1241         LOG.info(GrouperUtil.mapToString(infoMap));
1242       }
1243       
1244       return changedRecords;
1245     } catch (RuntimeException re) {
1246       
1247       LOG.error(GrouperUtil.mapToString(infoMap));
1248       throw re;
1249       
1250     }
1251   }
1252 
1253   /**
1254    * add a member to group, take into account if any default privs should be changed
1255    * @param subject to add
1256    * @param defaultPrivs if true, forget about all the other checked params
1257    * @param memberChecked
1258    * @param adminChecked
1259    * @param updateChecked
1260    * @param readChecked
1261    * @param viewChecked
1262    * @param optinChecked
1263    * @param optoutChecked
1264    * @param attrReadChecked
1265    * @param attrUpdateChecked
1266    * @param startDate on membership
1267    * @param endDate on membership
1268    * @param revokeIfUnchecked
1269    * @return if something was changed
1270    * @deprecated use addOrEditMember instead
1271    */
1272   @Deprecated
1273   public boolean addMember(final Subject subject, final boolean defaultPrivs,
1274       final boolean memberChecked, 
1275       final boolean adminChecked,
1276       final boolean updateChecked, final boolean readChecked, final boolean viewChecked,
1277       final boolean optinChecked, final boolean optoutChecked, final boolean attrReadChecked,
1278       final boolean attrUpdateChecked, final Date startDate, final Date endDate, final boolean revokeIfUnchecked) {
1279     return this.addOrEditMember(subject, defaultPrivs, memberChecked, adminChecked, updateChecked, 
1280         readChecked, viewChecked, optinChecked, optoutChecked, attrReadChecked, attrUpdateChecked, 
1281         startDate, endDate, revokeIfUnchecked);
1282   }
1283 
1284   /**
1285    * add a member to group, take into account if any default privs should be changed
1286    * @param subject to add
1287    * @param defaultPrivs if true, forget about all the other checked params
1288    * @param memberChecked
1289    * @param adminChecked
1290    * @param updateChecked
1291    * @param readChecked
1292    * @param viewChecked
1293    * @param optinChecked
1294    * @param optoutChecked
1295    * @param attrReadChecked
1296    * @param attrUpdateChecked
1297    * @param startDate on membership
1298    * @param endDate on membership
1299    * @param revokeIfUnchecked
1300    * @return if something was changed
1301    */
1302   public boolean addOrEditMember(final Subject subject, final boolean defaultPrivs,
1303       final boolean memberChecked, 
1304       final boolean adminChecked,
1305       final boolean updateChecked, final boolean readChecked, final boolean viewChecked,
1306       final boolean optinChecked, final boolean optoutChecked, final boolean attrReadChecked,
1307       final boolean attrUpdateChecked, final Date startDate, final Date endDate, final boolean revokeIfUnchecked) {
1308     
1309     return (Boolean)GrouperTransaction.callbackGrouperTransaction(GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, new GrouperTransactionHandler() {
1310       
1311       @Override
1312       public Object callback(GrouperTransaction grouperTransaction)
1313           throws GrouperDAOException {
1314 
1315         boolean hadChange = Group.this.addOrEditMember(subject, defaultPrivs, memberChecked, startDate, endDate, revokeIfUnchecked);
1316         
1317         if (!defaultPrivs) {
1318           
1319           //see if add or remove
1320           if (adminChecked) {
1321             hadChange = hadChange | Group.this.grantPriv(subject, AccessPrivilege.ADMIN, false);
1322           } else {
1323             if (revokeIfUnchecked) {
1324               hadChange = hadChange | Group.this.revokePriv(subject, AccessPrivilege.ADMIN, false);
1325             }
1326           }
1327 
1328           //see if add or remove
1329           if (updateChecked) {
1330             hadChange = hadChange | Group.this.grantPriv(subject, AccessPrivilege.UPDATE, false);
1331           } else  {
1332             if (revokeIfUnchecked) {
1333               hadChange = hadChange | Group.this.revokePriv(subject, AccessPrivilege.UPDATE, false);
1334             }
1335           }
1336 
1337           //see if add or remove
1338           if (readChecked) {
1339             hadChange = hadChange | Group.this.grantPriv(subject, AccessPrivilege.READ, false);
1340           } else {
1341             if (revokeIfUnchecked) {
1342               hadChange = hadChange | Group.this.revokePriv(subject, AccessPrivilege.READ, false);
1343             }
1344           }
1345 
1346           //see if add or remove
1347           if (viewChecked) {
1348             hadChange = hadChange | Group.this.grantPriv(subject, AccessPrivilege.VIEW, false);
1349           } else {
1350             if (revokeIfUnchecked) {
1351               hadChange = hadChange | Group.this.revokePriv(subject, AccessPrivilege.VIEW, false);
1352             }
1353           }
1354 
1355           //see if add or remove
1356           if (optinChecked) {
1357             hadChange = hadChange | Group.this.grantPriv(subject, AccessPrivilege.OPTIN, false);
1358           } else {
1359             if (revokeIfUnchecked) {
1360               hadChange = hadChange | Group.this.revokePriv(subject, AccessPrivilege.OPTIN, false);
1361             }
1362           }
1363 
1364           //see if add or remove
1365           if (optoutChecked) {
1366             hadChange = hadChange | Group.this.grantPriv(subject, AccessPrivilege.OPTOUT, false);
1367           } else {
1368             if (revokeIfUnchecked) {
1369               hadChange = hadChange | Group.this.revokePriv(subject, AccessPrivilege.OPTOUT, false);
1370             }
1371           }
1372 
1373           //see if add or remove
1374           if (attrReadChecked) {
1375             hadChange = hadChange | Group.this.grantPriv(subject, AccessPrivilege.GROUP_ATTR_READ, false);
1376           } else {
1377             if (revokeIfUnchecked) {
1378               hadChange = hadChange | Group.this.revokePriv(subject, AccessPrivilege.GROUP_ATTR_READ, false);
1379             }
1380           }
1381 
1382           //see if add or remove
1383           if (attrUpdateChecked) {
1384             hadChange = hadChange | Group.this.grantPriv(subject, AccessPrivilege.GROUP_ATTR_UPDATE, false);
1385           } else {
1386             if (revokeIfUnchecked) {
1387               hadChange = hadChange | Group.this.revokePriv(subject, AccessPrivilege.GROUP_ATTR_UPDATE, false);
1388             }
1389           }
1390         }
1391 
1392         return hadChange;
1393       }
1394     });
1395 
1396     
1397   }
1398 
1399   /**
1400    * add a member to group, take into account if any default privs should be changed
1401    * @param subject to add
1402    * @param defaultPrivs if true, forget about all the other checked params
1403    * @param memberChecked
1404    * @param startDate on membership
1405    * @param endDate on membership
1406    * @param revokeIfUnchecked
1407    * @return if something was changed
1408    * @deprecated use addOrEditMember instead
1409    */
1410   @Deprecated
1411   public boolean addMember(final Subject subject, final boolean defaultPrivs,
1412       final boolean memberChecked, final Date startDate, final Date endDate, final boolean revokeIfUnchecked) {
1413     return this.addOrEditMember(subject, defaultPrivs, memberChecked, startDate, endDate, revokeIfUnchecked);
1414   }
1415 
1416   /**
1417    * add a member to group, take into account if any default privs should be changed
1418    * @param subject to add
1419    * @param defaultPrivs if true, forget about all the other checked params
1420    * @param memberChecked
1421    * @param startDate on membership
1422    * @param endDate on membership
1423    * @param revokeIfUnchecked
1424    * @return if something was changed
1425    */
1426   public boolean addOrEditMember(final Subject subject, final boolean defaultPrivs,
1427       final boolean memberChecked, final Date startDate, final Date endDate, final boolean revokeIfUnchecked) {
1428     
1429     return (Boolean)GrouperTransaction.callbackGrouperTransaction(GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, new GrouperTransactionHandler() {
1430       
1431       @Override
1432       public Object callback(GrouperTransaction grouperTransaction)
1433           throws GrouperDAOException {
1434 
1435         boolean addedMember = defaultPrivs || memberChecked || startDate != null || endDate != null;
1436         
1437         boolean hadChange = false;
1438         
1439         if (addedMember) {
1440           
1441           Field field = Group.getDefaultList();
1442      
1443           Member member = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subject, true);
1444           
1445           //see if member
1446           Membership membership = 
1447               GrouperDAOFactory.getFactory().getMembership().findByGroupOwnerAndMemberAndFieldAndType( 
1448                   Group.this.getUuid(), member.getUuid(), field, MembershipType.IMMEDIATE.getTypeString(), false, false);
1449 
1450           if (membership != null) {
1451             
1452             if ( !Group.this.canHavePrivilege(GrouperSession.staticGrouperSession().getSubject(), field.getWritePrivilege(), false)) { 
1453 
1454               //if the subject operated on is the subject 
1455               if (SubjectHelper.eq(subject, GrouperSession.staticGrouperSession().getSubject())) {
1456                 
1457                 //if cant optin, then invalid
1458                 GrouperValidator v = CanOptinValidator.validate(Group.this, subject, field);
1459                 if (v.isInvalid()) {
1460                   throw new InsufficientPrivilegeException();
1461                 }
1462               } else {
1463                 throw new InsufficientPrivilegeException();
1464               }
1465             }
1466 
1467             //we are already a member, lets see if the dates match up
1468             boolean hasChange = false;
1469             if (!GrouperUtil.equals(startDate, membership.getEnabledTime())) {
1470               hasChange = true;
1471               membership.setEnabledTime(startDate == null ? null : new Timestamp(startDate.getTime()));
1472             }
1473             if (!GrouperUtil.equals(endDate, membership.getDisabledTime())) {
1474               hasChange = true;
1475               membership.setDisabledTime(endDate == null ? null : new Timestamp(endDate.getTime()));
1476             }
1477             //if a date didnt match up, then save the membership
1478             if (hasChange) {
1479               GrouperDAOFactory.getFactory().getMembership().update(membership);
1480             }
1481             return hasChange;
1482           }
1483 
1484           addedMember = Group.this.internal_addMember(subject, field, false, null, GrouperUtil.toTimestamp(startDate), 
1485               GrouperUtil.toTimestamp(endDate));
1486           hadChange = hadChange || addedMember;
1487           
1488         } else {
1489         
1490           //if they are doing custom privs, maybe they want member removed???
1491           if (revokeIfUnchecked) {
1492             hadChange = hadChange | Group.this.deleteMember(subject, false);
1493           }
1494         }
1495         
1496         return hadChange;
1497       }
1498     });
1499 
1500     
1501   }
1502   
1503   /**
1504    * Add a subject to this group as immediate member.
1505    * 
1506    * An immediate member is directly assigned to a group.
1507    * A composite group has no immediate members.  Note that a 
1508    * member can have 0 to 1 immediate memberships
1509    * to a single group, and 0 to many effective memberships to a group.
1510    * A group can have potentially unlimited effective 
1511    * memberships
1512    * 
1513    * <pre class="eg">
1514    * try {
1515    *   g.addMember(subj, f);
1516    * }
1517    * catch (InsufficientPrivilegeException eIP) {
1518    *   // Not privileged to add members 
1519    * }
1520    * catch (MemberAddException eMA) {
1521    *   // Unable to add member
1522    * } 
1523    * catch (SchemaException eS) {
1524    *   // Invalid Field
1525    * } 
1526    * </pre>
1527    * @param   subj  Add this {@link Subject}
1528    * @param   f     Add subject to this {@link Field}.
1529    * @param exceptionIfAlreadyMember if false, and subject is already a member,
1530    * then dont throw a MemberAddException if the member is already in the group
1531    * @param uuid is uuid or null for generated
1532    * @param startDate 
1533    * @param endDate 
1534    * @throws  InsufficientPrivilegeException
1535    * @throws  MemberAddException
1536    * @throws  SchemaException
1537    * @return false if it already existed, true if it didnt already exist
1538    */
1539   public boolean internal_addMember(final Subject subj, final Field f, 
1540       final boolean exceptionIfAlreadyMember, final String uuid, final Timestamp startDate, final Timestamp endDate)
1541     throws  InsufficientPrivilegeException,
1542             MemberAddException, SchemaException {
1543     return this.internal_addMember(subj, f, exceptionIfAlreadyMember, uuid, startDate, endDate, true);
1544   }
1545   
1546   
1547   /**
1548    * Add a subject to this group as immediate member.
1549    * 
1550    * An immediate member is directly assigned to a group.
1551    * A composite group has no immediate members.  Note that a 
1552    * member can have 0 to 1 immediate memberships
1553    * to a single group, and 0 to many effective memberships to a group.
1554    * A group can have potentially unlimited effective 
1555    * memberships
1556    * 
1557    * <pre class="eg">
1558    * try {
1559    *   g.addMember(subj, f);
1560    * }
1561    * catch (InsufficientPrivilegeException eIP) {
1562    *   // Not privileged to add members 
1563    * }
1564    * catch (MemberAddException eMA) {
1565    *   // Unable to add member
1566    * } 
1567    * catch (SchemaException eS) {
1568    *   // Invalid Field
1569    * } 
1570    * </pre>
1571    * @param   subj  Add this {@link Subject}
1572    * @param   f     Add subject to this {@link Field}.
1573    * @param exceptionIfAlreadyMember if false, and subject is already a member,
1574    * then dont throw a MemberAddException if the member is already in the group
1575    * @param uuid is uuid or null for generated
1576    * @param startDate 
1577    * @param endDate 
1578    * @throws  InsufficientPrivilegeException
1579    * @throws  MemberAddException
1580    * @throws  SchemaException
1581    * @return false if it already existed, true if it didnt already exist
1582    */
1583   public boolean internal_addMember(final Subject subj, final Field f, 
1584       final boolean exceptionIfAlreadyMember, final String uuid, final Timestamp startDate, final Timestamp endDate, final boolean checkSecurity)
1585     throws  InsufficientPrivilegeException,
1586             MemberAddException, SchemaException {
1587     final StopWatch sw = new StopWatch();
1588     sw.start();
1589     
1590     final String errorMessageSuffix = ", group name: " + this.name 
1591       + ", subject: " + GrouperUtil.subjectToString(subj) + ", field: " + (f == null ? null : f.getName());
1592   
1593     return (Boolean)HibernateSession.callbackHibernateSession(
1594         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
1595         new HibernateHandler() {
1596   
1597           public Object callback(HibernateHandlerBean hibernateHandlerBean)
1598               throws GrouperDAOException {
1599 
1600             try {
1601 
1602               hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
1603 
1604               if ( !FieldType.LIST.equals( f.getType() ) ) {
1605                 throw new SchemaException( E.FIELD_INVALID_TYPE + f.getType() );
1606               }
1607               if (checkSecurity && !Group.this.canWriteField(f) ) { 
1608                 GrouperValidator v = CanOptinValidator.validate(Group.this, subj, f);
1609                 if (v.isInvalid()) {
1610                   throw new InsufficientPrivilegeException();
1611                 }
1612               }
1613               //MCH 20090301: I would think this should be any member list (non privilege... not just default)
1614               if ( ( Group.getDefaultList().equals(f) ) && ( Group.this.hasComposite() ) ) {
1615                 throw new MemberAddException(E.GROUP_AMTC + ", " + Group.this.getName());
1616               }
1617               boolean doesntExist = true;
1618               Membership membership = null;
1619               
1620               try {
1621                 membership = Membership.internal_addImmediateMembership( GrouperSession.staticGrouperSession(), Group.this, 
1622                     subj, f , uuid, startDate, endDate);
1623                 
1624               } catch (MemberAddAlreadyExistsException memberAddAlreadyExistsException) {
1625                 if (exceptionIfAlreadyMember) {
1626                   throw memberAddAlreadyExistsException;
1627                 }
1628                 doesntExist = false;
1629               } catch (MembershipAlreadyExistsException membershipAlreadyExistsException) {
1630                 if (exceptionIfAlreadyMember) {
1631                   //convert to member add
1632                   throw new MemberAddAlreadyExistsException(membershipAlreadyExistsException);
1633                 }
1634                 doesntExist = false;
1635               } catch (HookVeto hookVeto) {
1636                 //pass this through
1637                 throw hookVeto;
1638               } catch (Exception exception) {
1639                 throw new RuntimeException(exception);
1640               }
1641               
1642               // if this is a wheel group then clear the wheel cache (granted if effective this wont work but cache lasts 10 seconds)
1643               if (
1644                   (GrouperConfig.retrieveConfig().propertyValueBoolean(GrouperConfig.PROP_USE_WHEEL_GROUP, false) &&
1645                       StringUtils.equals(Group.this.getName(), GrouperConfig.retrieveConfig().propertyValueString( GrouperConfig.PROP_WHEEL_GROUP )))
1646                || (GrouperConfig.retrieveConfig().propertyValueBoolean("groups.wheel.readonly.use", false) &&
1647                           StringUtils.equals(Group.this.getName(), GrouperConfig.retrieveConfig().propertyValueString("groups.wheel.readonly.group")))
1648                || (GrouperConfig.retrieveConfig().propertyValueBoolean("groups.wheel.viewonly.use", false) &&
1649                               StringUtils.equals(Group.this.getName(), GrouperConfig.retrieveConfig().propertyValueString("groups.wheel.viewonly.group")))) {
1650                 PrivilegeHelper.wheelMemberCacheClear();
1651               }
1652               
1653               if (doesntExist) {
1654                 
1655                 //this might be a wheel or a member of wheel...  maybe there is a more efficient way to do this...
1656                 PrivilegeHelper.flushCache();
1657                 
1658                 EVENT_LOG.groupAddMember(GrouperSession.staticGrouperSession(), Group.this.getName(), subj, f, sw);
1659                 
1660                 RulesMembershipBeanershipBean.html#RulesMembershipBean">RulesMembershipBean rulesMembershipBean = new RulesMembershipBean(membership, Group.this, subj);
1661 
1662                 //if we are in the default list, then fire a rule
1663                 if (StringUtils.equals(f.getUuid(), Group.getDefaultList().getUuid())) {
1664                   
1665                   //fire rules directly connected to this membership add
1666                   RuleEngine.fireRule(RuleCheckType.membershipAdd, rulesMembershipBean);
1667                   //fire rules related to add in stem
1668                   RuleEngine.fireRule(RuleCheckType.membershipAddInFolder, rulesMembershipBean);
1669 
1670                 }
1671 
1672                 //fire rules related to subject assign in folder
1673                 RuleEngine.fireRule(RuleCheckType.subjectAssignInStem, rulesMembershipBean);
1674 
1675                 if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
1676                   
1677                   AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.MEMBERSHIP_GROUP_ADD, "id", 
1678                       membership == null ? null : membership.getUuid(), "fieldId", f.getUuid(),
1679                           "fieldName", f.getName(), "memberId",  membership.getMemberUuid(),
1680                           "membershipType", membership.getType(), 
1681                           "groupId", Group.this.getUuid(), "groupName", Group.this.getName());
1682                           
1683                   auditEntry.setDescription("Added membership: group: " + Group.this.getName()
1684                       + ", subject: " + subj.getSource().getId() + "." + subj.getId() + ", field: "
1685                       + f.getName());
1686                   auditEntry.saveOrUpdate(true);
1687                 }
1688 
1689                 // just make sure again that the owner group isn't a composite,
1690                 // it could have been made into one while adding this membership..
1691                 // note that since the commit isn't done yet, this issue can still happen,
1692                 // but it would hopefully be much less likely..
1693                 if ((Group.getDefaultList().equals(f)) && (Group.this.hasComposite())) {
1694                   throw new IllegalStateException("Group (name=" + Group.this.getName() + ") turned into a composite while adding an immediate membership.");
1695                 }
1696               }
1697               sw.stop();
1698               return doesntExist;
1699             } catch (RuntimeException re) {
1700               GrouperUtil.injectInException(re, errorMessageSuffix);
1701               throw re;
1702             }
1703           }
1704         });
1705     
1706   } // public void addMember(subj, f)
1707 
1708   /**
1709    * Add an additional group type.
1710    * <pre class="eg">
1711    * try {
1712    *   GroupType custom = GroupTypeFinder.find("custom type");
1713    *   g.addType(custom);
1714    * }
1715    * catch (GroupModifyException eGM) {
1716    *   // Unable to add type 
1717    * }
1718    * catch (InsufficientPrivilegeException eIP) {
1719    *   // Not privileged to add type
1720    * }
1721    * catch (SchemaException eS) {
1722    *   // Cannot add system-maintained types
1723    * }
1724    * </pre>
1725    * @param   type  The {@link GroupType} to add.
1726    * @throws  GroupModifyException if unable to add type.
1727    * @throws  InsufficientPrivilegeException if subject not root-like.
1728    * @throws  SchemaException if attempting to add a system group type.
1729    * @deprecated
1730    */
1731   public void addType(GroupType type) 
1732     throws  GroupModifyException,
1733             InsufficientPrivilegeException,
1734             SchemaException {
1735     addType(type, true);
1736   }
1737   
1738   /**
1739    * Add an additional group type.
1740    * <pre class="eg">
1741    * try {
1742    *   GroupType custom = GroupTypeFinder.find("custom type");
1743    *   g.addType(custom);
1744    * }
1745    * catch (GroupModifyException eGM) {
1746    *   // Unable to add type 
1747    * }
1748    * catch (InsufficientPrivilegeException eIP) {
1749    *   // Not privileged to add type
1750    * }
1751    * catch (SchemaException eS) {
1752    *   // Cannot add system-maintained types
1753    * }
1754    * </pre>
1755    * @param   type  The {@link GroupType} to add.
1756    * @param exceptionIfAlreadyHasType 
1757    * @throws  GroupModifyException if unable to add type.
1758    * @throws  InsufficientPrivilegeException if subject not root-like.
1759    * @throws  SchemaException if attempting to add a system group type.
1760    * @return if it was added or not
1761    * @deprecated
1762    */
1763   public boolean addType(final GroupType type, final boolean exceptionIfAlreadyHasType) 
1764     throws  GroupModifyException, InsufficientPrivilegeException, SchemaException {
1765     return internal_addType(type, null, exceptionIfAlreadyHasType);
1766   }
1767   
1768   /**
1769    * @param type
1770    * @param groupTypeAssignmentId
1771    * @param exceptionIfAlreadyHasType
1772    * @return if it was added or not
1773    * @throws GroupModifyException
1774    * @throws InsufficientPrivilegeException
1775    * @throws SchemaException
1776    */
1777   public boolean internal_addType(final GroupType type, final String groupTypeAssignmentId, final boolean exceptionIfAlreadyHasType) 
1778     throws  GroupModifyException, InsufficientPrivilegeException, SchemaException {
1779     
1780     return (Boolean)HibernateSession.callbackHibernateSession(
1781         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT, new HibernateHandler() {
1782 
1783       public Object callback(HibernateHandlerBean hibernateHandlerBean)
1784           throws GrouperDAOException {
1785         
1786         hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
1787 
1788         StopWatch sw = new StopWatch();
1789         sw.start();
1790         if ( Group.this.hasType(type, false) ) {
1791           if (exceptionIfAlreadyHasType) {
1792             throw new GroupModifyException(E.GROUP_HAS_TYPE + ", " + type);
1793           }
1794           return false;
1795         }
1796         if ( type.isSystemType() ) {
1797           throw new SchemaException("cannot edit system group types");
1798         }
1799         if ( !PrivilegeHelper.canAdmin( GrouperSession.staticGrouperSession(), Group.this, 
1800             GrouperSession.staticGrouperSession().getSubject() ) ) {
1801           throw new InsufficientPrivilegeException(E.CANNOT_ADMIN);
1802         }
1803         Group.this.internal_getGroupTypeAssignments();
1804         Group.this.getTypesDb();
1805 
1806         if (GrouperLoader.isDryRun()) {
1807           GrouperLoader.dryRunWriteLine("Group add type: " + type.getName());
1808         } else {
1809           
1810           AttributeAssign groupTypeAssignment = null;
1811           try {
1812             groupTypeAssignment = Group.this.getAttributeDelegate().internal_assignAttributeHelper(null, type.getAttributeDefName(), true, groupTypeAssignmentId, null).getAttributeAssign();
1813             Group.this.types.put(type.getName(), type.getAttributeDefName());
1814             Group.this.typeAssignments.put(type.getName(), groupTypeAssignment);
1815 
1816             // force refresh
1817             Group.this.attributes = null;
1818             Group.this.getAttributesDb();
1819             
1820           } catch (RuntimeException re) {
1821             throw re;
1822           }
1823           if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
1824             AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_TYPE_ASSIGN, "id", 
1825                 groupTypeAssignment.getId(), "groupId", Group.this.getUuid(), 
1826                 "groupName", Group.this.getName(), "typeId", type.getUuid(), "typeName", type.getName());
1827             auditEntry.setDescription("Assigned group type: " + name + ", typeId: " + type.getUuid() 
1828                 + ", to group: " + Group.this.getName() + ", groupId: " + Group.this.getUuid());
1829             auditEntry.saveOrUpdate(true);
1830           }
1831         }
1832 
1833         
1834         sw.stop();
1835         EventLog.info(
1836             GrouperSession.staticGrouperSession(),
1837           M.GROUP_ADDTYPE + Quote.single(Group.this.getName()) + " type=" + Quote.single( type.getName() ),
1838           sw
1839         );
1840         return true;
1841       }
1842     });
1843   } 
1844 
1845   /**
1846    * Check whether the {@link Subject} that loaded this {@link Group} can
1847    * read the specified {@link Field}.
1848    * <pre class="eg">
1849    * try {
1850    *   boolean rv = g.canReadField(f);
1851    * }
1852    * catch (SchemaException eS) {
1853    *   // invalid field
1854    * }
1855    * </pre>
1856    * @param   f   Check privileges on this {@link Field}.
1857    * @return  True if {@link Subject} can read {@link Field}, false otherwise.
1858    * @throws  IllegalArgumentException if null {@link Field}
1859    * @throws  SchemaException if invalid {@link Field}
1860    * @since   1.0
1861    */
1862   public boolean canReadField(Field f) 
1863     throws  IllegalArgumentException,
1864             SchemaException
1865   {
1866     return this.canReadField(GrouperSession.staticGrouperSession().getSubject(), f);
1867   } // public boolean canReadField(f)
1868 
1869   /**
1870    * Check whether the specified {@link Subject} can read the specified {@link Field}.
1871    * <pre class="eg">
1872    * try {
1873    *   boolean rv = g.canReadField(subj, f);
1874    * }
1875    * catch (SchemaException eS) {
1876    *   // invalid field
1877    * }
1878    * </pre>
1879    * @param   subj  Check privileges for this {@link Subject}.
1880    * @param   f     Check privileges on this {@link Field}.
1881    * @return  True if {@link Subject} can read {@link Field}, false otherwise.
1882    * @throws  IllegalArgumentException if null {@link Subject} or {@link Field}
1883    * @throws  SchemaException if invalid {@link Field} or {@link Subject}.
1884    * @since   1.2.0
1885    */
1886   public boolean canReadField(Subject subj, Field f) 
1887     throws  IllegalArgumentException,
1888             SchemaException
1889   {
1890     GrouperValidator v = NotNullValidator.validate(subj);
1891     if (v.isInvalid()) {
1892       throw new IllegalArgumentException( "subject: " + v.getErrorMessage() );
1893     }
1894     v = NotNullValidator.validate(f);
1895     if (v.isInvalid()) {
1896       throw new IllegalArgumentException( "field: " + v.getErrorMessage() );
1897     }
1898     v = FieldTypeValidator.validate(f);
1899     if (v.isInvalid()) {
1900       throw new SchemaException( v.getErrorMessage() );
1901     }
1902     GroupType groupType = f.getGroupType(false);
1903     if (groupType != null && !this.hasType(groupType)) {
1904       throw new SchemaException(E.INVALID_GROUP_TYPE + groupType.toString());
1905     }
1906     try {
1907       PrivilegeHelper.dispatch( GrouperSession.staticGrouperSession(), this, subj, f.getReadPriv() );
1908       return true;
1909     }
1910     catch (InsufficientPrivilegeException eIP) {
1911       return false;
1912     }
1913   } // public boolean canReadField(subj, f)
1914 
1915   /**
1916    * Check whether the {@link Subject} that loaded this {@link Group} can
1917    * write the specified {@link Field}.
1918    * <pre class="eg">
1919    * try {
1920    *   boolean rv = g.canWriteField(f);
1921    * }
1922    * catch (SchemaException eS) {
1923    *   // invalid field
1924    * }
1925    * </pre>
1926    * @param   f   Check privileges on this {@link Field}.
1927    * @return  True if {@link Subject} can write {@link Field}, false otherwise.
1928    * @throws  IllegalArgumentException if null {@link Field}
1929    * @throws  SchemaException if invalid {@link Field}
1930    * @since   1.0
1931    */
1932   public boolean canWriteField(Field f) 
1933     throws  IllegalArgumentException,
1934             SchemaException
1935   {
1936     return this.canWriteField(GrouperSession.staticGrouperSession().getSubject(), f);
1937   } // public boolean canWriteField(f)
1938 
1939   /**
1940    * Check whether the specified {@link Subject} can write the specified {@link Field}.
1941    * <pre class="eg">
1942    * try {
1943    *   boolean rv = g.canWriteField(subj, f);
1944    * }
1945    * catch (SchemaException eS) {
1946    *   // invalid field
1947    * }
1948    * </pre>
1949    * @param   subj  Check privileges for this {@link Subject}.
1950    * @param   f     Check privileges on this {@link Field}.
1951    * @return  True if {@link Subject} can write {@link Field}, false otherwise.
1952    * @throws  IllegalArgumentException if null {@link Subject} or {@link Field}
1953    * @throws  SchemaException if invalid {@link Field}
1954    * @since   1.0
1955    */
1956   public boolean canWriteField(Subject subj, Field f) 
1957     throws  IllegalArgumentException,
1958             SchemaException
1959   {
1960     return this.internal_canWriteField(subj, f);
1961   } // public boolean canWriteField(subj, f)
1962 
1963   /**
1964    * keep track of if we are in a delete so hooks can 
1965    */
1966   private static ThreadLocal<Boolean> threadLocalInGroupDelete = new InheritableThreadLocal<Boolean>();
1967   
1968   /**
1969    * see if we are in the middle of a delete (e.g. for hook)
1970    * @return true if delete is occurring
1971    */
1972   public static boolean deleteOccuring() {
1973     Boolean deleteOccuring = threadLocalInGroupDelete.get();
1974     if (deleteOccuring != null) {
1975       return deleteOccuring;
1976     }
1977     return false;
1978   }
1979   
1980   /**
1981    * Delete this group from the Groups Registry.
1982    * <pre class="eg">
1983    * try {
1984    *   g.delete();
1985    * }
1986    * catch (GroupDeleteException e0) {
1987    *   // Unable to delete group
1988    * }
1989    * catch (InsufficientPrivilegeException e1) {
1990    *   // Not privileged to delete this group
1991    * }
1992    * </pre>
1993    * @throws  GroupDeleteException
1994    * @throws  InsufficientPrivilegeException
1995    */
1996   public void delete() throws GroupDeleteException, InsufficientPrivilegeException {
1997     final String errorMessageSuffix = ", stem name: " + this.name + ", group extension: " + this.extension
1998       + ", group dExtension: " + this.displayExtension + ", uuid: " + this.uuid + ", ";
1999 
2000     HibernateSession.callbackHibernateSession(
2001         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
2002         new HibernateHandler() {
2003 
2004           public Object callback(HibernateHandlerBean hibernateHandlerBean)
2005               throws GrouperDAOException {
2006 
2007             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
2008 
2009             StopWatch sw = new StopWatch();
2010             sw.start();
2011             GrouperSession.validate( GrouperSession.staticGrouperSession() );
2012             if ( !PrivilegeHelper.canAdmin( GrouperSession.staticGrouperSession(), Group.this, 
2013                 GrouperSession.staticGrouperSession().getSubject() ) ) {
2014               throw new InsufficientPrivilegeException(
2015                   E.CANNOT_ADMIN + errorMessageSuffix);
2016             }
2017             try {
2018 
2019               threadLocalInGroupDelete.set(true);
2020 
2021               // ... And delete composite mship if it exists
2022               if (Group.this.hasComposite()) {
2023                 Group.this.deleteCompositeMember();
2024               }
2025               
2026               // ... And delete composite mship if it exists if this group is a member
2027               // GRP-1704: when deleting a group, delete any composites that is a member of
2028               if (GrouperConfig.retrieveConfig().propertyValueBoolean("grouper.delete.compositeMembershipsOnGroupDelete", true)) {
2029                 for (Composite composite : GrouperUtil.nonNull(GrouperDAOFactory.getFactory().getComposite().findAsFactor( Group.this ))) {
2030                   composite.getOwnerGroup().deleteCompositeMember();
2031                 }
2032               }
2033               
2034               // ... And delete all memberships - as root
2035               // Deletes (and saves) now happen within internal_deleteAllFieldType().  See GRP-254.
2036               GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
2037                 
2038                 public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
2039                   
2040                   Membership.internal_deleteAllFieldType( 
2041                     GrouperSession.staticGrouperSession().internal_getRootSession(), Group.this, FieldType.LIST );
2042                   
2043                   return null;
2044                 }
2045               });
2046 
2047               //delete any attributes on this group, this is done as root
2048               GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
2049                 
2050                 public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
2051                   Set<AttributeAssign> attributeAssigns = GrouperDAOFactory.getFactory().getAttributeAssign().findByOwnerGroupId(Group.this.getId());
2052                   
2053                   for (AttributeAssign attributeAssign : attributeAssigns) {
2054                     attributeAssign.delete();
2055                   }
2056                   return null;
2057                 }
2058               });  
2059                 
2060               // Delete privileges where this group is the member.  This is done as root..
2061               Subject groupSubject = Group.this.toSubject();
2062               GrouperSession.staticGrouperSession().internal_getRootSession()
2063                   .getAccessResolver().revokeAllPrivilegesForSubject(groupSubject);
2064               GrouperSession.staticGrouperSession().internal_getRootSession()
2065                   .getNamingResolver().revokeAllPrivilegesForSubject(groupSubject);
2066               GrouperSession.staticGrouperSession().internal_getRootSession()
2067                 .getAttributeDefResolver().revokeAllPrivilegesForSubject(groupSubject);
2068               
2069               // Revoke all access privs
2070               // GRP-1193 - cant delete composite group
2071               Group.this._revokeAllAccessPrivs();
2072 
2073               //deletes.add(this);            // ... And add the group last for good luck    
2074               String theName = Group.this.getName(); // Preserve name for logging
2075               GrouperDAOFactory.getFactory().getGroup().delete(Group.this);
2076 
2077               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
2078                 AuditEntry auditEntry = null;
2079                 if (Group.this.typeOfGroup == TypeOfGroup.entity) {
2080                   
2081                   auditEntry = new AuditEntry(AuditTypeBuiltin.ENTITY_DELETE, "id", 
2082                       Group.this.getUuid(), "name", Group.this.getName(), "parentStemId", Group.this.getParentUuid(), 
2083                       "displayName", Group.this.getDisplayName(), "description", Group.this.getDescription());
2084                   auditEntry.setDescription("Deleted entity: " + Group.this.getName());
2085 
2086                 } else {
2087                   auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_DELETE, "id", 
2088                       Group.this.getUuid(), "name", Group.this.getName(), "parentStemId", Group.this.getParentUuid(), 
2089                       "displayName", Group.this.getDisplayName(), "description", Group.this.getDescription());
2090                   auditEntry.setDescription("Deleted group: " + Group.this.getName());
2091                   
2092                 }
2093                 auditEntry.saveOrUpdate(true);
2094               }
2095               
2096               sw.stop();
2097               EventLog.info(GrouperSession.staticGrouperSession(), M.GROUP_DEL + Quote.single(theName), sw);
2098           } catch (InsufficientPrivilegeException eDAO) {
2099             throw new GrouperException( eDAO );
2100           } catch (GrouperDAOException eDAO) {
2101             throw new GroupDeleteException( eDAO.getMessage() + errorMessageSuffix, eDAO );
2102           } catch (RevokePrivilegeException eRP) {
2103             throw new GroupDeleteException(eRP.getMessage() + errorMessageSuffix, eRP);
2104           } catch (SchemaException eS) {
2105             throw new GroupDeleteException(eS.getMessage() + errorMessageSuffix, eS);
2106           } finally {
2107             threadLocalInGroupDelete.remove();
2108           }
2109           return null;
2110         }
2111       });
2112   }
2113 
2114   /**
2115    * Delete a group attribute.
2116    * <pre class="eg">
2117    * try {
2118    *   g.deleteAttribute(attribute);
2119    * }
2120    * catch (GroupModifyException e0) {
2121    *   // Unable to modify group
2122    * }
2123    * catch (InsufficientPrivilegeException e1) {
2124    *   // Not privileged to delete this attribute
2125    * }
2126    * </pre>
2127    * @param   attrName  Delete this attribute.
2128    * @throws  AttributeNotFoundException
2129    * @throws  GroupModifyException
2130    * @throws  InsufficientPrivilegeException
2131    * @deprecated
2132    */
2133   public void deleteAttribute(final String attrName) 
2134     throws  AttributeNotFoundException,
2135             GroupModifyException, 
2136             InsufficientPrivilegeException {
2137     this.deleteAttribute(attrName, false);
2138   }
2139   
2140   /**
2141    * Delete a group attribute.
2142    * <pre class="eg">
2143    * try {
2144    *   g.deleteAttribute(attribute);
2145    * }
2146    * catch (GroupModifyException e0) {
2147    *   // Unable to modify group
2148    * }
2149    * catch (InsufficientPrivilegeException e1) {
2150    *   // Not privileged to delete this attribute
2151    * }
2152    * </pre>
2153    * @param   attrName  Delete this attribute.
2154    * @param failOnRequiredAttribute true if exception when attribute is required
2155    * @throws  AttributeNotFoundException
2156    * @throws  GroupModifyException
2157    * @throws  InsufficientPrivilegeException
2158    * @deprecated
2159    */
2160   public void deleteAttribute(final String attrName, final boolean failOnRequiredAttribute) 
2161     throws  AttributeNotFoundException,
2162             GroupModifyException, 
2163             InsufficientPrivilegeException {
2164     
2165     if (failOnRequiredAttribute) {
2166       throw new RuntimeException("Parameter failOnRequiredAttribute must be false.");
2167     }
2168     
2169     HibernateSession.callbackHibernateSession(
2170         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
2171         new HibernateHandler() {
2172 
2173           public Object callback(HibernateHandlerBean hibernateHandlerBean)
2174               throws GrouperDAOException {
2175             try {
2176 
2177               hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
2178 
2179               StopWatch sw = new StopWatch();
2180               sw.start();
2181               NotNullOrEmptyValidator v = NotNullOrEmptyValidator.validate(attrName);
2182               if (v.isInvalid()) {
2183                 throw new AttributeNotFoundException(E.INVALID_ATTR_NAME + attrName);
2184               }
2185               
2186               Group.this.getAttributesMap(false);
2187               if (Group.this.attributes.containsKey(attrName)) {
2188                 AttributeAssignValue attribute = Group.this.attributes.get(attrName);
2189                 String val = attribute.getValueString(); // for logging
2190                 
2191                 attribute.getAttributeAssign().getOwnerAttributeAssign().getAttributeDelegate().assertCanUpdateAttributeDefName(attribute.getAttributeAssign().getAttributeDefName());
2192 
2193                 // delete attribute assignment using new attribute framework
2194                 attribute.getAttributeAssign().delete();
2195 
2196                 Group.this.attributes.remove(attrName);
2197 
2198                 sw.stop();
2199                 EVENT_LOG.groupDelAttr(GrouperSession.staticGrouperSession(), Group.this.getName(), attrName, val, sw);
2200                 
2201                 if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
2202                   
2203                   AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_ATTRIBUTE_DELETE, "id", 
2204                       Group.this.getUuid(), "groupId", Group.this.getUuid(), 
2205                       "groupName", Group.this.getName(), "fieldId", Group.this.getDescription(),
2206                       "fieldName", attrName,  "value", val);
2207                   
2208                   auditEntry.setDescription("Deleted group attribute: " + attrName + " on group: " 
2209                       + Group.this.getName() + " value: " + val);
2210                   auditEntry.saveOrUpdate(true);
2211                 }
2212 
2213                 
2214               }
2215               else {
2216                 throw new AttributeNotFoundException("Attribute not exist: " + attrName);
2217               }
2218             }
2219             catch (GrouperDAOException eDAO) {
2220               throw new GroupModifyException( eDAO.getMessage(), eDAO );
2221             }
2222             catch (InsufficientPrivilegeException eIP) {
2223               throw eIP;
2224             }
2225             catch (SchemaException eS) {
2226               throw new AttributeNotFoundException(eS.getMessage(), eS);
2227             }
2228             return null;
2229           }
2230         });
2231     
2232   } // public void deleteAttribute(attr)
2233   
2234   /**
2235    * Delete a {@link Composite} membership from this group.
2236    * 
2237    * A composite group is composed of two groups and a set operator 
2238    * (stored in grouper_composites table)
2239    * (e.g. union, intersection, etc).  A composite group has no immediate members.
2240    * All subjects in a composite group are effective members.
2241    * 
2242    * <pre class="eg">
2243    * try {
2244    *   g.deleteCompositeMember();
2245    * }
2246    * catch (InsufficientPrivilegeException eIP) {
2247    *   // Not privileged to delete members
2248    * }
2249    * catch (MemberDeleteException eMD) {
2250    *   // Unable to delete composite membership
2251    * } 
2252    * </pre>
2253    * @throws  InsufficientPrivilegeException
2254    * @throws  MemberDeleteException
2255    * @since   1.0
2256    */
2257   public void deleteCompositeMember()
2258     throws  InsufficientPrivilegeException,
2259             MemberDeleteException  {
2260     
2261     
2262     final StringBuilder errorMessageSuffix = new StringBuilder("group name: " + Group.this.name);
2263     
2264     HibernateSession.callbackHibernateSession(
2265         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
2266         new HibernateHandler() {
2267 
2268           public Object callback(HibernateHandlerBean hibernateHandlerBean)
2269           throws GrouperDAOException {
2270             try {
2271     
2272               hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
2273 
2274               StopWatch sw  = new StopWatch();
2275               sw.start();
2276 
2277               String leftGroupName = null;
2278               Composite composite = null;
2279               try {
2280                 composite = Group.this.getComposite(true);
2281               } catch (CompositeNotFoundException cnfe) {
2282                 GrouperUtil.injectInException(cnfe, errorMessageSuffix.toString());
2283                 throw new MemberDeleteException(E.GROUP_DCFC + ", " + cnfe.getMessage(), cnfe);
2284               }
2285               
2286               try { 
2287                 leftGroupName = composite.getLeftGroup().getName();
2288               } catch (GroupNotFoundException gnfe) {
2289                 leftGroupName = composite.getLeftFactorUuid();
2290               }
2291               
2292               String rightGroupName = null;
2293               
2294               try { 
2295                 rightGroupName = composite.getRightGroup().getName();
2296               } catch (GroupNotFoundException gnfe) {
2297                 rightGroupName = composite.getRightFactorUuid();
2298               }
2299               errorMessageSuffix.append(", compositeType: " + composite.getTypeDb()
2300               + ", left group name: " + leftGroupName
2301               + ", right group name: " + rightGroupName);
2302               
2303               if ( !Group.this.canWriteField( GrouperSession.staticGrouperSession().getSubject(), Group.getDefaultList() ) ) {
2304                 throw new InsufficientPrivilegeException();
2305               }
2306 
2307               GrouperDAOFactory.getFactory().getComposite().delete(composite);
2308 
2309               EVENT_LOG.groupDelComposite( GrouperSession.staticGrouperSession(), composite, sw );
2310               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
2311                 
2312                 AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_COMPOSITE_DELETE, "id", 
2313                     composite.getUuid(), "ownerId", Group.this.getUuid(), "ownerName", Group.this.getName(), "leftFactorId", 
2314                     composite.getLeftFactorUuid(), "leftFactorName", leftGroupName, "rightFactorId", composite.getRightFactorUuid(), 
2315                     "rightFactorName", rightGroupName, "type", composite.getTypeDb());
2316                 auditEntry.setDescription("Deleted composite: " + Group.this.getName() + " was " 
2317                     + leftGroupName + " " + composite.getTypeDb() + " " + rightGroupName);
2318                 auditEntry.saveOrUpdate(true);
2319               }
2320               sw.stop();
2321               return null;
2322             } catch (GrouperDAOException eDAO) {
2323               GrouperUtil.injectInException(eDAO, errorMessageSuffix.toString());
2324               throw new MemberDeleteException(eDAO.getMessage(), eDAO);
2325             } catch (RuntimeException re) {
2326               GrouperUtil.injectInException(re, errorMessageSuffix.toString());
2327               throw re;
2328             }
2329           }
2330         });
2331   }
2332   
2333   /** 
2334    * Delete a member from this group, and member must be immediate
2335    * member.  Will not delete the effective membership.
2336    * 
2337    * An immediate member is directly assigned to a group.
2338    * A composite group has no immediate members.  Note that a 
2339    * member can have 0 to 1 immediate memberships
2340    * to a single group, and 0 to many effective memberships to a group.
2341    * A group can have potentially unlimited effective 
2342    * memberships
2343    * 
2344    * <pre class="eg">
2345    * try {
2346    *   g.deleteMember(member);
2347    * } 
2348    * catch (InsufficientPrivilegeException eIP) {
2349    *   // Not privileged to delete this subject
2350    * }
2351    * catch (MemberDeleteException eMD) {
2352    *   // Unable to delete subject
2353    * }
2354    * </pre>
2355    * @param   member  Delete this {@link Member}
2356    * @param exceptionIfAlreadyDeleted throw exception if already deleted
2357    * @throws  InsufficientPrivilegeException
2358    * @throws  MemberDeleteException
2359    * @return false if it was already deleted, true if it wasnt already deleted
2360    */
2361   public boolean deleteMember(Member member, boolean exceptionIfAlreadyDeleted)
2362     throws  InsufficientPrivilegeException, MemberDeleteException {
2363     try {
2364       return this.deleteMember(member, getDefaultList(), exceptionIfAlreadyDeleted);
2365     }
2366     catch (SchemaException eS) {
2367       throw new MemberDeleteException(eS.getMessage(), eS);
2368     }
2369 
2370   }
2371   
2372   /** 
2373    * Delete a member from this group, and member must be immediate
2374    * member.  Will not delete the effective membership.
2375    * 
2376    * An immediate member is directly assigned to a group.
2377    * A composite group has no immediate members.  Note that a 
2378    * member can have 0 to 1 immediate memberships
2379    * to a single group, and 0 to many effective memberships to a group.
2380    * A group can have potentially unlimited effective 
2381    * memberships
2382    * 
2383    * <pre class="eg">
2384    * try {
2385    *   g.deleteMember(member);
2386    * } 
2387    * catch (InsufficientPrivilegeException eIP) {
2388    *   // Not privileged to delete this subject
2389    * }
2390    * catch (MemberDeleteException eMD) {
2391    *   // Unable to delete subject
2392    * }
2393    * </pre>
2394    * @param   member  Delete this {@link Member}
2395    * @throws  InsufficientPrivilegeException
2396    * @throws  MemberDeleteException
2397    */
2398   public void deleteMember(Member member)
2399     throws  InsufficientPrivilegeException, MemberDeleteException {
2400     deleteMember(member, true);
2401   }
2402 
2403   /** 
2404    * Delete a member from this group, and member must be immediate
2405    * member.  Will not delete the effective membership.
2406    * 
2407    * An immediate member is directly assigned to a group.
2408    * A composite group has no immediate members.  Note that a 
2409    * member can have 0 to 1 immediate memberships
2410    * to a single group, and 0 to many effective memberships to a group.
2411    * A group can have potentially unlimited effective 
2412    * memberships
2413    * 
2414    * <pre class="eg">
2415    * try {
2416    *   g.deleteMember(m, f);
2417    * } 
2418    * catch (InsufficientPrivilegeException eIP) {
2419    *   // Not privileged to delete this subject
2420    * }
2421    * catch (MemberDeleteException eMD) {
2422    *   // Unable to delete subject
2423    * }
2424    * </pre>
2425    * @param   member  Delete this {@link Member}.
2426    * @param   f     Delete subject from this {@link Field}.
2427    * @param exceptionIfAlreadyDeleted 
2428    * @return false if it was already deleted, true if it wasnt already deleted
2429    * @throws  InsufficientPrivilegeException
2430    * @throws  MemberDeleteException
2431    * @throws  SchemaException
2432    */
2433   public boolean  deleteMember(Member member, Field f, boolean exceptionIfAlreadyDeleted) 
2434       throws  InsufficientPrivilegeException, MemberDeleteException, SchemaException {
2435     
2436     Subject lazySubject = new LazySubject(member);
2437     
2438     return deleteMember(lazySubject, f, exceptionIfAlreadyDeleted);
2439     
2440   }
2441 
2442   /** 
2443    * Delete a member from this group, and member must be immediate
2444    * member.  Will not delete the effective membership.
2445    * 
2446    * An immediate member is directly assigned to a group.
2447    * A composite group has no immediate members.  Note that a 
2448    * member can have 0 to 1 immediate memberships
2449    * to a single group, and 0 to many effective memberships to a group.
2450    * A group can have potentially unlimited effective 
2451    * memberships
2452    * 
2453    * <pre class="eg">
2454    * try {
2455    *   g.deleteMember(m, f);
2456    * } 
2457    * catch (InsufficientPrivilegeException eIP) {
2458    *   // Not privileged to delete this subject
2459    * }
2460    * catch (MemberDeleteException eMD) {
2461    *   // Unable to delete subject
2462    * }
2463    * </pre>
2464    * @param   member  Delete this {@link Member}.
2465    * @param   f     Delete subject from this {@link Field}.
2466    * @throws  InsufficientPrivilegeException
2467    * @throws  MemberDeleteException
2468    * @throws  SchemaException
2469    */
2470   public void deleteMember(Member member, Field f) 
2471       throws  InsufficientPrivilegeException, MemberDeleteException, SchemaException {
2472     deleteMember(member, f, true);
2473   }
2474 
2475   
2476   
2477   /** 
2478    * Delete a subject from this group, and subject must be immediate
2479    * member.  Will not delete the effective membership.
2480    * 
2481    * An immediate member is directly assigned to a group.
2482    * A composite group has no immediate members.  Note that a 
2483    * member can have 0 to 1 immediate memberships
2484    * to a single group, and 0 to many effective memberships to a group.
2485    * A group can have potentially unlimited effective 
2486    * memberships
2487    * 
2488    * <pre class="eg">
2489    * try {
2490    *   g.deleteMember(subj);
2491    * } 
2492    * catch (InsufficientPrivilegeException eIP) {
2493    *   // Not privileged to delete this subject
2494    * }
2495    * catch (MemberDeleteException eMD) {
2496    *   // Unable to delete subject
2497    * }
2498    * </pre>
2499    * @param   subj  Delete this {@link Subject}
2500    * @throws  InsufficientPrivilegeException
2501    * @throws  MemberDeleteException
2502    */
2503   public void deleteMember(Subject subj)
2504     throws  InsufficientPrivilegeException,
2505             MemberDeleteException {
2506     deleteMember(subj, true);
2507   }
2508   
2509   /** 
2510    * Delete a subject from this group, and subject must be immediate
2511    * member.  Will not delete the effective membership.
2512    * 
2513    * An immediate member is directly assigned to a group.
2514    * A composite group has no immediate members.  Note that a 
2515    * member can have 0 to 1 immediate memberships
2516    * to a single group, and 0 to many effective memberships to a group.
2517    * A group can have potentially unlimited effective 
2518    * memberships
2519    * 
2520    * <pre class="eg">
2521    * try {
2522    *   g.deleteMember(subj);
2523    * } 
2524    * catch (InsufficientPrivilegeException eIP) {
2525    *   // Not privileged to delete this subject
2526    * }
2527    * catch (MemberDeleteException eMD) {
2528    *   // Unable to delete subject
2529    * }
2530    * </pre>
2531    * @param   subj  Delete this {@link Subject}
2532    * @param exceptionIfAlreadyDeleted 
2533    * @return false if it was already deleted, true if it wasnt already deleted
2534    * @throws  InsufficientPrivilegeException
2535    * @throws  MemberDeleteException
2536    */
2537   public boolean deleteMember(Subject subj, boolean exceptionIfAlreadyDeleted)
2538     throws  InsufficientPrivilegeException,
2539             MemberDeleteException
2540   {
2541     try {
2542       return this.deleteMember(subj, getDefaultList(), exceptionIfAlreadyDeleted);
2543     }
2544     catch (SchemaException eS) {
2545       throw new MemberDeleteException(eS.getMessage(), eS);
2546     }
2547   }
2548 
2549   /** 
2550    * Delete a subject from this group, and subject must be immediate
2551    * member.  Will not delete the effective membership.
2552    * 
2553    * An immediate member is directly assigned to a group.
2554    * A composite group has no immediate members.  Note that a 
2555    * member can have 0 to 1 immediate memberships
2556    * to a single group, and 0 to many effective memberships to a group.
2557    * A group can have potentially unlimited effective 
2558    * memberships
2559    * 
2560    * <pre class="eg">
2561    * try {
2562    *   g.deleteMember(m, f);
2563    * } 
2564    * catch (InsufficientPrivilegeException eIP) {
2565    *   // Not privileged to delete this subject
2566    * }
2567    * catch (MemberDeleteException eMD) {
2568    *   // Unable to delete subject
2569    * }
2570    * </pre>
2571    * @param   subj  Delete this {@link Subject}.
2572    * @param   f     Delete subject from this {@link Field}.
2573    * @throws  InsufficientPrivilegeException
2574    * @throws  MemberDeleteException
2575    * @throws  SchemaException
2576    */
2577   public void deleteMember(Subject subj, Field f) 
2578     throws  InsufficientPrivilegeException, 
2579             MemberDeleteException,
2580             SchemaException {
2581     deleteMember(subj, f, true);
2582   }
2583   
2584   /** 
2585    * Delete a subject from this group, and subject must be immediate
2586    * member.  Will not delete the effective membership.
2587    * 
2588    * An immediate member is directly assigned to a group.
2589    * A composite group has no immediate members.  Note that a 
2590    * member can have 0 to 1 immediate memberships
2591    * to a single group, and 0 to many effective memberships to a group.
2592    * A group can have potentially unlimited effective 
2593    * memberships
2594    * 
2595    * <pre class="eg">
2596    * try {
2597    *   g.deleteMember(m, f);
2598    * } 
2599    * catch (InsufficientPrivilegeException eIP) {
2600    *   // Not privileged to delete this subject
2601    * }
2602    * catch (MemberDeleteException eMD) {
2603    *   // Unable to delete subject
2604    * }
2605    * </pre>
2606    * @param   subj  Delete this {@link Subject}.
2607    * @param   f     Delete subject from this {@link Field}.
2608    * @param exceptionIfAlreadyDeleted true if an exception should be thrown
2609    * if the member is already deleted
2610    * @return false if it was already deleted, true if it wasnt already deleted
2611    * @throws  InsufficientPrivilegeException
2612    * @throws  MemberDeleteException
2613    * @throws  SchemaException
2614    */
2615   public boolean  deleteMember(final Subject subj, final Field f, final boolean exceptionIfAlreadyDeleted) 
2616     throws  InsufficientPrivilegeException, 
2617             MemberDeleteException,
2618             SchemaException { 
2619     return this.internal_deleteMember(subj, f, exceptionIfAlreadyDeleted, true);
2620   }
2621   
2622   /** 
2623    * Delete a subject from this group, and subject must be immediate
2624    * member.  Will not delete the effective membership.
2625    * 
2626    * An immediate member is directly assigned to a group.
2627    * A composite group has no immediate members.  Note that a 
2628    * member can have 0 to 1 immediate memberships
2629    * to a single group, and 0 to many effective memberships to a group.
2630    * A group can have potentially unlimited effective 
2631    * memberships
2632    * 
2633    * <pre class="eg">
2634    * try {
2635    *   g.deleteMember(m, f);
2636    * } 
2637    * catch (InsufficientPrivilegeException eIP) {
2638    *   // Not privileged to delete this subject
2639    * }
2640    * catch (MemberDeleteException eMD) {
2641    *   // Unable to delete subject
2642    * }
2643    * </pre>
2644    * @param   subj  Delete this {@link Subject}.
2645    * @param   f     Delete subject from this {@link Field}.
2646    * @param exceptionIfAlreadyDeleted true if an exception should be thrown
2647    * if the member is already deleted
2648    * @param checkSecurity false if should not check security
2649    * @return false if it was already deleted, true if it wasnt already deleted
2650    * @throws  InsufficientPrivilegeException
2651    * @throws  MemberDeleteException
2652    * @throws  SchemaException
2653    */
2654   public boolean internal_deleteMember(final Subject subj, final Field f, final boolean exceptionIfAlreadyDeleted, final boolean checkSecurity)  {
2655 
2656     final StopWatch sw  = new StopWatch();
2657     sw.start();
2658     
2659     final String errorMessageSuffix = ", group name: " + this.name 
2660       + ", subject: " + GrouperUtil.subjectToString(subj) + ", field: " + (f == null ? null : f.getName());
2661 
2662     return (Boolean)HibernateSession.callbackHibernateSession(
2663         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
2664       new HibernateHandler() {
2665 
2666         public Object callback(HibernateHandlerBean hibernateHandlerBean)
2667             throws GrouperDAOException {
2668 
2669           boolean notAlreadyDeleted = true;
2670           try {
2671 
2672             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
2673 
2674             if ( !FieldType.LIST.equals( f.getType() ) ) {
2675               throw new SchemaException( E.FIELD_INVALID_TYPE + f.getType() );
2676             }
2677             if (checkSecurity && !Group.this.canWriteField(f) ) {
2678               GrouperValidator v = CanOptoutValidator.validate(Group.this, subj, f);
2679               if (v.isInvalid()) {
2680                 throw new InsufficientPrivilegeException(errorMessageSuffix);
2681               }
2682             }
2683             if ( (f.equals( Group.getDefaultList() ) ) && ( Group.this.hasComposite() ) ) {
2684               throw new MemberDeleteException(E.GROUP_DMFC);
2685             }
2686 
2687             Membership membership = Membership.internal_delImmediateMembership( 
2688                 GrouperSession.staticGrouperSession(), Group.this, subj, f );
2689 
2690             sw.stop();
2691             if (notAlreadyDeleted) {
2692               
2693               //this might be a wheel or a member of wheel...  maybe there is a more efficient way to do this...
2694               PrivilegeHelper.flushCache();
2695 
2696               EVENT_LOG.groupDelMember(GrouperSession.staticGrouperSession(), 
2697                   Group.this.getName(), subj, f, sw);
2698 
2699               //if we are in the default list, then fire a rule
2700               if (StringUtils.equals(f.getUuid(), Group.getDefaultList().getUuid())) {
2701                 RulesMembershipBeanershipBean.html#RulesMembershipBean">RulesMembershipBean rulesMembershipBean = new RulesMembershipBean(membership, Group.this, subj);
2702                 //fire rules directly connected to this membership remove
2703                 RuleEngine.fireRule(RuleCheckType.membershipRemove, rulesMembershipBean);
2704                 //fire rules related to remove in stem
2705                 RuleEngine.fireRule(RuleCheckType.membershipRemoveInFolder, rulesMembershipBean);
2706 
2707               }
2708               
2709               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
2710 
2711                 AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.MEMBERSHIP_GROUP_DELETE, "id", 
2712                     membership == null ? null : membership.getUuid(), "fieldId", f.getUuid(),
2713                         "fieldName", f.getName(), "memberId",  membership.getMemberUuid(),
2714                         "membershipType", membership.getType(), 
2715                         "groupId", Group.this.getUuid(), "groupName", Group.this.getName());
2716                         
2717                 auditEntry.setDescription("Deleted membership: group: " + Group.this.getName()
2718                     + ", subject: " + subj.getSource().getId() + "." + subj.getId() + ", field: "
2719                     + f.getName());
2720                 auditEntry.saveOrUpdate(true);
2721               }
2722 
2723               
2724             }
2725           } catch (GrouperDAOException eDAO) {
2726             throw new MemberDeleteException( eDAO.getMessage() + ", " + errorMessageSuffix, eDAO );
2727           } catch (MemberDeleteAlreadyDeletedException mdade) {
2728             if (exceptionIfAlreadyDeleted) {
2729               GrouperUtil.injectInException(mdade, errorMessageSuffix);
2730               throw mdade;
2731             }
2732             notAlreadyDeleted = false;
2733           }
2734           return notAlreadyDeleted;
2735         }
2736         });
2737   } // public void deleteMember(subj, f)
2738 
2739   /**
2740    * Delete a group type.
2741    * <pre class="eg">
2742    * try {
2743    *   GroupType custom = GroupTypeFinder.find("custom type");
2744    *   g.deleteType(custom);
2745    * }
2746    * catch (GroupModifyException eGM) {
2747    *   // Unable to delete type 
2748    * }
2749    * catch (InsufficientPrivilegeException eIP) {
2750    *   // Not privileged to add type
2751    * }
2752    * catch (SchemaException eS) {
2753    *   // Cannot delete system-maintained types
2754    * }
2755    * </pre>
2756    * @param   type  The {@link GroupType} to add.
2757    * @throws  GroupModifyException if unable to delete type.
2758    * @throws  InsufficientPrivilegeException if subject not root-like.
2759    * @throws  SchemaException if attempting to delete a system group type.
2760    * @deprecated
2761    */
2762   public void deleteType(final GroupType type) 
2763     throws  GroupModifyException,
2764             InsufficientPrivilegeException,
2765             SchemaException {
2766     
2767     final String typeString = type == null ? null : type.getName();
2768     HibernateSession.callbackHibernateSession(
2769         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT, new HibernateHandler() {
2770 
2771       public Object callback(HibernateHandlerBean hibernateHandlerBean)
2772           throws GrouperDAOException {
2773         
2774         try {
2775 
2776           hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
2777 
2778           StopWatch sw = new StopWatch();
2779           sw.start();
2780           if ( !Group.this.hasType(type, false) ) {
2781             throw new GroupModifyException("does not have type: " + typeString);
2782           }
2783           if ( type.isSystemType() ) {
2784             throw new SchemaException("cannot edit system group types: " + typeString);
2785           }
2786           if ( !PrivilegeHelper.canAdmin( GrouperSession.staticGrouperSession(), Group.this, GrouperSession.staticGrouperSession().getSubject() ) ) {
2787             throw new InsufficientPrivilegeException(E.CANNOT_ADMIN);
2788           }
2789   
2790           Group.this.internal_getGroupTypeAssignments();
2791           Group.this.getTypesDb();
2792           
2793           AttributeAssign oldAssign = Group.this.typeAssignments.get(type.getName());
2794           
2795           Group.this.types.remove(type.getName());
2796           Group.this.typeAssignments.remove(type.getName());
2797           
2798           Group.this.getAttributeDelegate().removeAttribute(type.getAttributeDefName());
2799           
2800           // force refresh
2801           Group.this.attributes = null;
2802           Group.this.getAttributesDb();
2803           
2804           if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
2805             AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_TYPE_UNASSIGN, "id", 
2806                 oldAssign.getId(), "groupId", Group.this.getUuid(), 
2807                 "groupName", Group.this.getName(), "typeId", type.getUuid(), "typeName", type.getName());
2808             auditEntry.setDescription("Unasssigned group type: " + name + ", typeId: " + type.getUuid() 
2809                 + ", to group: " + Group.this.getName() + ", groupId: " + Group.this.getUuid());
2810             auditEntry.saveOrUpdate(true);
2811           }
2812           
2813           sw.stop();
2814           EventLog.info(
2815             GrouperSession.staticGrouperSession(),
2816             M.GROUP_DELTYPE + Quote.single(Group.this.getName()) + " type=" + Quote.single( type.getName() ),
2817             sw
2818           );
2819           return null;
2820         } catch (GrouperDAOException eDAO) {
2821           String msg = E.GROUP_TYPEDEL + type + ": "; 
2822           msg += eDAO.getMessage();
2823           LOG.error(msg);
2824           throw new GroupModifyException(msg, eDAO);
2825         } catch (RuntimeException re) {
2826           GrouperUtil.injectInException(re, "Problem with type: " + typeString);
2827           throw re;
2828         }
2829       }
2830     });
2831   } 
2832 
2833   /**
2834    * Get subjects with the ADMIN privilege on this group.
2835    * <pre class="eg">
2836    * Set admins = g.getAdmins();
2837    * </pre>
2838    * @return  Set of subjects with ADMIN
2839    * @throws  GrouperException
2840    */
2841   public Set<Subject> getAdmins() 
2842     throws  GrouperException
2843   {
2844     return GrouperSession.staticGrouperSession().getAccessResolver().getSubjectsWithPrivilege(this, AccessPrivilege.ADMIN);
2845   }
2846 
2847   /**
2848    * get the value of an attribute, if not there return null
2849    * @param attributeName
2850    * @return the attribute value
2851    * @deprecated use getAttributeValue()
2852    */
2853   @Deprecated
2854   public String getAttributeOrNull(String attributeName) {
2855     try {
2856       return this.getAttribute(attributeName);
2857     } catch (AttributeNotFoundException anfe) {
2858       return null;
2859     }
2860   }
2861   
2862   /**
2863    * if attribute or field name is a field name, call that getter with reflection.
2864    * If not, then call getAttributeValue()
2865    * @param attributeOrFieldName
2866    * @param checkSecurity
2867    * @param exceptionIfAttributeNotFound
2868    * @return the value
2869    * @deprecated
2870    */
2871   public String getAttributeOrFieldValue(String attributeOrFieldName, boolean checkSecurity, boolean exceptionIfAttributeNotFound) {
2872     
2873     if (INTERNAL_FIELD_ATTRIBUTES.contains(attributeOrFieldName)) {
2874       return (String)GrouperUtil.fieldValue(this, attributeOrFieldName);
2875     }
2876     return this.getAttributeValue(attributeOrFieldName, checkSecurity, exceptionIfAttributeNotFound);
2877     
2878   }
2879   
2880   /**
2881    * get the value of an attribute, if not there return the empty string.
2882    * or exception if expected to be there.
2883    * 
2884    * @param attributeName
2885    * @param exceptionIfNotFound
2886    * @param checkSecurity
2887    * @return the attribute value or null if not there and not expecting exception.
2888    * @deprecated
2889    */
2890   public String getAttributeValue(String attributeName, 
2891       boolean checkSecurity, boolean exceptionIfNotFound) {
2892 
2893     NotNullOrEmptyValidator v = NotNullOrEmptyValidator.validate(attributeName);
2894     if (v.isInvalid()) {
2895       throw new AttributeNotFoundException(E.INVALID_ATTR_NAME + attributeName);
2896     }
2897     
2898     //init
2899     this.getAttributesMap(false);
2900     
2901     AttributeAssignValue value = this.attributes.get(attributeName);
2902 
2903     if (value == null) {
2904 
2905       // Group does not have attribute.  If attribute is not valid for Group,
2906       // throw AttributeNotFoundException.  Otherwise, return an empty string.
2907             
2908       String attributeDefPrefix = GrouperConfig.retrieveConfig().propertyValueStringRequired("legacyAttribute.attributeDef.prefix");
2909       AttributeDefName legacyAttribute = GrouperDAOFactory.getFactory().getAttributeDefName().findLegacyAttributeByName(attributeName, false);
2910       
2911       if (legacyAttribute == null) {
2912         throw new AttributeNotFoundException("Cant find attribute: " + attributeName);
2913       }
2914       
2915       AttributeDef legacyAttributeDef = legacyAttribute.getAttributeDef();
2916       String groupTypeName = legacyAttributeDef.getExtension().substring(attributeDefPrefix.length());
2917       
2918       AttributeAssign groupTypeAssignment = Group.this.internal_getGroupTypeAssignments().get(groupTypeName);
2919       if (groupTypeAssignment == null) {
2920         throw new AttributeNotFoundException("Group " + Group.this.getName() + " doesn't have attribute: " + attributeName);
2921       }
2922 
2923       if (exceptionIfNotFound) {
2924         throw new AttributeNotFoundException("Cant find attribute value: " + attributeName);
2925       }
2926       return "";
2927     }
2928     
2929     if (checkSecurity) {
2930       try {
2931         value.getAttributeAssign().getOwnerAttributeAssign().getAttributeDelegate().assertCanReadAttributeDef(value.getAttributeAssign().getAttributeDef());
2932       } catch (InsufficientPrivilegeException e) {
2933         if (exceptionIfNotFound) {
2934           throw new AttributeNotFoundException("Cant read attribute: " + attributeName);
2935         }
2936         
2937         return "";
2938       } catch (AttributeDefNotFoundException e) {
2939         if (exceptionIfNotFound) {
2940           throw new AttributeNotFoundException("Cant read attribute: " + attributeName);
2941         }
2942         
2943         return "";
2944       }
2945     }
2946     
2947     return StringUtils.defaultString(value.getValueString());
2948   }
2949   
2950   /**
2951    * Get attribute value.
2952    * <pre class="eg">
2953    * try {
2954    *   String value = g.getAttribute(attribute);
2955    * }
2956    * catch (AttributeNotFoundException e) {
2957    *   // Group doesn't have attribute
2958    * }
2959    * </pre>
2960    * @param   attr  Get value of this attribute.
2961    * @return  Attribute value.  or throw AttributeNotFoundException if not there.
2962    * The value will be the emprty string if it is null
2963    * @throws  AttributeNotFoundException
2964    * @Deprecated use getAttributeValue
2965    */
2966   @Deprecated
2967   public String getAttribute(String attr) 
2968     throws  AttributeNotFoundException {
2969 
2970     return getAttributeValue(attr, false, false);
2971   }
2972 
2973   /**
2974    * Get {@link Composite} {@link Member}s of this group.
2975    * 
2976    * A composite group is composed of two groups and a set operator 
2977    * (stored in grouper_composites table)
2978    * (e.g. union, intersection, etc).  A composite group has no immediate members.
2979    * All subjects in a composite group are effective members.
2980    * 
2981    * <pre class="eg">
2982    * Set members = g.getCompositeMembers();
2983    * </pre>
2984    * @param queryOptions 
2985    * @return  A set of {@link Member} objects.
2986    * @since   1.0
2987    */
2988   public Set<Member> getCompositeMembers(QueryOptions queryOptions) {
2989     return getCompositeMembers(Group.getDefaultList(), null, queryOptions);
2990   } // public Set getCompositeMembers()
2991 
2992   /**
2993    * @param attr
2994    * @return the value
2995    */
2996   @SuppressWarnings("unused")
2997   private String _internal_getAttributeBuiltIn(String attr) {
2998     if (GrouperConfig.retrieveConfig().propertyValueBoolean("groups.allow.attribute.access.1.4", false)) {
2999       
3000       if (StringUtils.equals(FIELD_NAME, attr)) {
3001         return this.getName();
3002       }
3003       if (StringUtils.equals(FIELD_EXTENSION, attr)) {
3004         return this.getExtension();
3005       }
3006       if (StringUtils.equals(FIELD_DISPLAY_NAME, attr)) {
3007         return this.getDisplayName();
3008       }
3009       if (StringUtils.equals(FIELD_DISPLAY_EXTENSION, attr)) {
3010         return this.getDisplayExtension();
3011       }
3012       if (StringUtils.equals(FIELD_DESCRIPTION, attr)) {
3013         return this.getDescription();
3014       }
3015       throw new RuntimeException("Not expecting attribute: " + attr);
3016       
3017     }
3018     throw new RuntimeException("Cannot access built in attribute: " + attr + " from getAttributes anymore, " +
3019     		"use getter directly (e.g. getName(), getDisplayName()).  Or you can enable this (deprecated) with " +
3020     		"grouper.properties setting groups.allow.attribute.access.1.4=true");
3021   }
3022 
3023   /**
3024    * Get all attributes and values.
3025    * <pre class="eg">
3026    * Map attributes = g.getAttributes();
3027    * </pre>
3028    * @return  A map of attributes and values.
3029    * @deprecated use getAttributesMap
3030    */
3031   @Deprecated
3032   public Map<String, String> getAttributes() {
3033     //init
3034     Map<String, Attribute> results = this.getAttributesMap(true);
3035     
3036     Map<String, String> map = new HashMap<String, String>();
3037     for (String key : results.keySet()) {
3038       map.put(key, results.get(key).getValue());
3039     }
3040     return map;
3041     //Subject currentSubject = GrouperSession.staticGrouperSession().getSubject();
3042     
3043     //if (GrouperConfig.retrieveConfig().propertyValueBoolean("groups.allow.attribute.access.1.4", false)) {
3044     //
3045     //  boolean canReadGroup = this.hasRead(currentSubject);
3046     //  boolean canViewGroup = this.hasView(currentSubject);
3047     //  
3048     //  if (canViewGroup) {
3049     //    {
3050     //      String theName = this.getName();
3051     //      if (!StringUtils.isBlank(theName)) {
3052     //        filtered.put("name", theName);
3053     //      }
3054     //    }
3055     //    {
3056     //      String theDisplayName = this.getDisplayName();
3057     //      if (!StringUtils.isBlank(theDisplayName)) {
3058     //        filtered.put("displayName", theDisplayName);
3059     //      }
3060     //    }
3061     //    {
3062     //      String theExtension = this.getExtension();
3063     //      if (!StringUtils.isBlank(theExtension)) {
3064     //        filtered.put("extension", theExtension);
3065     //     }
3066     //    }
3067     //    {
3068     //      String theDisplayExtension = this.getDisplayExtension();
3069     //      if (!StringUtils.isBlank(theDisplayExtension)) {
3070     //        filtered.put("displayExtension", theDisplayExtension);
3071     //      }
3072     //    }
3073     //  }
3074     //  if (canReadGroup) {
3075     //    {
3076     //      String theDescription = this.getDescription();
3077     //      if (!StringUtils.isBlank(theDescription)) {
3078     //        filtered.put("description", theDescription);
3079     //      }
3080     //    }
3081     //  }
3082     //}
3083   } // public Map getAttributes()
3084 
3085   /**
3086    * Get {@link Composite} {@link Member}s of this group.
3087    * 
3088    * A composite group is composed of two groups and a set operator 
3089    * (stored in grouper_composites table)
3090    * (e.g. union, intersection, etc).  A composite group has no immediate members.
3091    * All subjects in a composite group are effective members.
3092    * 
3093    * <pre class="eg">
3094    * Set members = g.getCompositeMembers();
3095    * </pre>
3096    * @return  A set of {@link Member} objects.
3097    * @since   1.0
3098    */
3099   public Set<Member> getCompositeMembers() {
3100     return this.getCompositeMembers(null);
3101   } // public Set getCompositeMembers()
3102 
3103   /**
3104    * Get {@link Composite} {@link Membership}s of this group.
3105    * 
3106    * A composite group is composed of two groups and a set operator 
3107    * (stored in grouper_composites table)
3108    * (e.g. union, intersection, etc).  A composite group has no immediate members.
3109    * All subjects in a composite group are effective members.
3110    * 
3111    * A membership is the object which represents a join of member
3112    * and group.  Has metadata like type and creator,
3113    * and, if an effective membership, the parent membership
3114    * 
3115    * <pre class="eg">
3116    * Set mships = g.getCompositeMembers();
3117    * </pre>
3118    * @return  A set of {@link Membership} objects.
3119    * @since   1.0
3120    */
3121   public Set<Membership> getCompositeMemberships() {
3122     return MembershipFinder.internal_findAllByGroupOwnerAndFieldAndType(
3123       GrouperSession.staticGrouperSession(), this, Group.getDefaultList(), MembershipType.COMPOSITE.getTypeString()
3124     );
3125   } // public Set getCompositeMemberships()
3126 
3127   /**
3128    * Get subject that created this group.
3129    * <pre class="eg">
3130    * // Get creator of this group.
3131    * try {
3132    *   Subject creator = g.getCreateSubject();
3133    * }
3134    * catch (SubjectNotFoundException e) {
3135    *   // Couldn't find subject
3136    * }
3137    * </pre>
3138    * @return  {@link Subject} that created this group.
3139    * @throws  SubjectNotFoundException
3140    */
3141   public Subject getCreateSubject() 
3142     throws SubjectNotFoundException
3143   {
3144     if ( this.subjectCache.containsKey(KEY_CREATOR) ) {
3145       return this.subjectCache.get(KEY_CREATOR);
3146     }
3147     try {
3148       // when called from "GrouperSubject" there is no attached session
3149       Member _m = GrouperDAOFactory.getFactory().getMember().findByUuid( this.getCreatorUuid(), true );
3150       this.subjectCache.put( 
3151         KEY_CREATOR, SubjectFinder.findByIdAndSource( _m.getSubjectId(), _m.getSubjectSourceId() , true) 
3152       );
3153       return this.subjectCache.get(KEY_CREATOR);
3154     }
3155     catch (MemberNotFoundException eMNF) {
3156       throw new SubjectNotFoundException( eMNF.getMessage(), eMNF );
3157     }
3158     catch (SourceUnavailableException eSU) {
3159       throw new SubjectNotFoundException( eSU.getMessage(), eSU );
3160     }
3161     catch (SubjectNotUniqueException eSNU) {
3162       throw new SubjectNotFoundException( eSNU.getMessage(), eSNU );
3163     }
3164   } // public Subject getCreateSubject() 
3165   
3166   /**
3167    * Get creation time for this group.
3168    * <pre class="eg">
3169    * // Get create time.
3170    * Date created = g.getCreateTime();
3171    * </pre>
3172    * @return  {@link Date} that this group was created.
3173    */
3174   public Date getCreateTime() {
3175     return new Date(this.getCreateTimeLong());
3176   } // public Date getCreateTime()
3177 
3178   /**
3179    * Get group description.
3180    * <pre class="eg">
3181    * String description = g.getDescription();
3182    * </pre>
3183    * @return  Group's <i>description</i> or an empty string if no value set.
3184    */
3185   public String getDescription() {
3186     return GrouperUtil.defaultString(this.description);
3187   }
3188 
3189   /**
3190    * Get group description for hibernate.
3191    * @return  Group's <i>description</i> or an empty string if no value set.
3192    */
3193   public String getDescriptionDb() {
3194     return this.description;
3195   } 
3196 
3197   /**
3198    * list of internal field attributes, access with method so it can lazy load
3199    */
3200   public static final Set<String> INTERNAL_FIELD_ATTRIBUTES = Collections.unmodifiableSet(
3201       GrouperUtil.toSet(FIELD_DESCRIPTION, FIELD_NAME, FIELD_EXTENSION, 
3202       FIELD_DISPLAY_EXTENSION, FIELD_DISPLAY_NAME));
3203   
3204   /**
3205    * see if field attribute (name, description, extension, displayName, displayExtension)
3206    * @param attributeName
3207    * @return true if so
3208    */
3209   public static boolean _internal_fieldAttribute(String attributeName) {
3210     return INTERNAL_FIELD_ATTRIBUTES.contains(attributeName);
3211   }
3212   
3213   /**
3214    * Get group displayExtension.
3215    * <pre class="eg">
3216    * String displayExtn = g.getDisplayExtension();
3217    * </pre>
3218    * @return  Gruop displayExtension.
3219    * @throws  GrouperException
3220    */
3221   public String getDisplayExtension() {
3222     // We don't validate privs here because if one has retrieved a group then one
3223     // has at least VIEW.
3224     String val = this.displayExtension;
3225     if ( val == null || GrouperConfig.EMPTY_STRING.equals(val) ) {
3226       //  A group without this attribute is VERY faulty
3227       LOG.fatal(E.GROUP_NODE);
3228       throw new GrouperException(E.GROUP_NODE);
3229     }
3230     return val;
3231   } // public String getDisplayExtension()
3232 
3233   /**
3234    * Get group displayName.
3235    * <pre class="eg">
3236    * String displayName = g.getDisplayName();
3237    * </pre>
3238    * @return  Group displayName.
3239    * @throws  GrouperException
3240    */
3241   public String getDisplayName() 
3242     throws  GrouperException
3243   {
3244     // We don't validate privs here because if one has retrieved a group then one
3245     // has at least VIEW.
3246     String val = this.displayName;
3247     if ( val == null || GrouperConfig.EMPTY_STRING.equals(val) ) {
3248       //  A group without this attribute is VERY faulty
3249       LOG.fatal(E.GROUP_NODN);
3250       throw new GrouperException(E.GROUP_NODN);
3251     }
3252     return val;
3253   } // public String getDisplayName()
3254 
3255   /**
3256    * Get effective members of this group.
3257    * 
3258    * An effective member has an indirect membership to a group
3259    * (e.g. in a group within a group).  All subjects in a
3260    * composite group are effective members (since the composite
3261    * group has two groups and a set operator and no other immediate
3262    * members).  Note that a member can have 0 to 1 immediate memberships
3263    * to a single group, and 0 to many effective memberships to a group.
3264    * 'group within a group' can be nested to any level so long as it does 
3265    * not become circular.  A group can have potentially unlimited effective 
3266    * memberships
3267    * 
3268    * <pre class="eg">
3269    * Set effectives = g.getEffectiveMembers();
3270    * </pre>
3271    * @return  A set of {@link Member} objects.
3272    * @throws  GrouperException
3273    */
3274   public Set<Member> getEffectiveMembers() 
3275     throws  GrouperException
3276   {
3277     try {
3278       return this.getEffectiveMembers(getDefaultList());
3279     }
3280     catch (SchemaException eS) {
3281       // If we don't have "members" we have serious issues
3282       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
3283       LOG.fatal(msg);
3284       throw new GrouperException(msg, eS);
3285     }
3286   } // public Set getEffectiveMembership()
3287 
3288 
3289   /**
3290    * Get effective members of this group.
3291    * 
3292    * An effective member has an indirect membership to a group
3293    * (e.g. in a group within a group).  All subjects in a
3294    * composite group are effective members (since the composite
3295    * group has two groups and a set operator and no other immediate
3296    * members).  Note that a member can have 0 to 1 immediate memberships
3297    * to a single group, and 0 to many effective memberships to a group.
3298    * 'group within a group' can be nested to any level so long as it does 
3299    * not become circular.  A group can have potentially unlimited effective 
3300    * memberships
3301    * 
3302    * <pre class="eg">
3303    * Set effectives = g.getEffectiveMembers(f);
3304    * </pre>
3305    * @param   f Get members in this list field.
3306    * @return  A set of {@link Member} objects.
3307    * @throws  SchemaException
3308    */
3309   public Set<Member> getEffectiveMembers(Field f) 
3310     throws  SchemaException
3311   {
3312     return this.getEffectiveMembers(f, null);
3313   }  // public Set getEffectiveMembers(f)
3314 
3315   /**
3316    * Get effective members of this group.
3317    * 
3318    * An effective member has an indirect membership to a group
3319    * (e.g. in a group within a group).  All subjects in a
3320    * composite group are effective members (since the composite
3321    * group has two groups and a set operator and no other immediate
3322    * members).  Note that a member can have 0 to 1 immediate memberships
3323    * to a single group, and 0 to many effective memberships to a group.
3324    * 'group within a group' can be nested to any level so long as it does 
3325    * not become circular.  A group can have potentially unlimited effective 
3326    * memberships
3327    * 
3328    * <pre class="eg">
3329    * Set effectives = g.getEffectiveMembers(f);
3330    * </pre>
3331    * @param   f Get members in this list field.
3332    * @param queryOptions 
3333    * @return  A set of {@link Member} objects.
3334    * @throws  SchemaException
3335    */
3336   public Set<Member> getEffectiveMembers(Field f, QueryOptions queryOptions) 
3337     throws  SchemaException
3338   {
3339     return getEffectiveMembers(f, null, queryOptions);
3340   }  // public Set getEffectiveMembers(f)
3341 
3342   /**
3343    * Get effective members of this group.
3344    * 
3345    * An effective member has an indirect membership to a group
3346    * (e.g. in a group within a group).  All subjects in a
3347    * composite group are effective members (since the composite
3348    * group has two groups and a set operator and no other immediate
3349    * members).  Note that a member can have 0 to 1 immediate memberships
3350    * to a single group, and 0 to many effective memberships to a group.
3351    * 'group within a group' can be nested to any level so long as it does 
3352    * not become circular.  A group can have potentially unlimited effective 
3353    * memberships
3354    * 
3355    * <pre class="eg">
3356    * Set effectives = g.getEffectiveMembers(f);
3357    * </pre>
3358    * @param   f Get members in this list field.
3359    * @param sources sources to filter by, or null for all
3360    * @param queryOptions 
3361    * @return  A set of {@link Member} objects.
3362    * @throws  SchemaException
3363    */
3364   public Set<Member> getEffectiveMembers(Field f, Set<Source> sources, QueryOptions queryOptions) 
3365     throws  SchemaException
3366   {
3367     return MemberFinder.internal_findMembersByType(
3368         GrouperSession.staticGrouperSession(), this, f, MembershipType.EFFECTIVE.getTypeString(), sources, queryOptions,
3369         null, null, null);
3370   }  // public Set getEffectiveMembers(f)
3371 
3372   /**
3373    * Get effective memberships of this group.
3374    * 
3375    * An effective member has an indirect membership to a group
3376    * (e.g. in a group within a group).  All subjects in a
3377    * composite group are effective members (since the composite
3378    * group has two groups and a set operator and no other immediate
3379    * members).  Note that a member can have 0 to 1 immediate memberships
3380    * to a single group, and 0 to many effective memberships to a group.
3381    * 'group within a group' can be nested to any level so long as it does 
3382    * not become circular.  A group can have potentially unlimited effective 
3383    * memberships
3384    * 
3385    * <pre class="eg">
3386    * Set effectives = g.getEffectiveMemberships();
3387    * </pre>
3388    * @return  A set of {@link Membership} objects.
3389    * @throws  GrouperException
3390    */
3391   public Set<Membership> getEffectiveMemberships() 
3392     throws  GrouperException
3393   {
3394     try {
3395       return this.getEffectiveMemberships(getDefaultList());
3396     }
3397     catch (SchemaException eS) {
3398       // If we don't have "members" we have serious issues
3399       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
3400       LOG.fatal(msg);
3401       throw new GrouperException(msg, eS);
3402     }
3403   } // public Set getEffectiveMembership()
3404 
3405   /**
3406    * Get effective memberships of this group.
3407    * 
3408    * An effective member has an indirect membership to a group
3409    * (e.g. in a group within a group).  All subjects in a
3410    * composite group are effective members (since the composite
3411    * group has two groups and a set operator and no other immediate
3412    * members).  Note that a member can have 0 to 1 immediate memberships
3413    * to a single group, and 0 to many effective memberships to a group.
3414    * 'group within a group' can be nested to any level so long as it does 
3415    * not become circular.  A group can have potentially unlimited effective 
3416    * memberships
3417    * 
3418    * <pre class="eg">
3419    * Set memberships = g.getEffectiveMemberships(f);
3420    * </pre>
3421    * @param   f Get memberships in this list field.
3422    * @return  A set of {@link Membership} objects.
3423    * @throws  SchemaException
3424    */
3425   public Set<Membership> getEffectiveMemberships(Field f) 
3426     throws  SchemaException
3427   {
3428     return MembershipFinder.internal_findAllByGroupOwnerAndFieldAndType(
3429       GrouperSession.staticGrouperSession(), this, f, MembershipType.EFFECTIVE.getTypeString()
3430     );
3431   } // public Set getEffectiveMemberships(f)
3432 
3433   /**
3434    * Get group extension.
3435    * <pre class="eg">
3436    * String extension = g.getExtension();
3437    * </pre>
3438    * @return  Group extension.
3439    * @throws  GrouperException
3440    */
3441   public String getExtension() {
3442     // We don't validate privs here because if one has retrieved a group then one
3443     // has at least VIEW.
3444     String val = this.extension;
3445     if ( val == null || GrouperConfig.EMPTY_STRING.equals(val) ) {
3446       //  A group without this attribute is VERY faulty
3447       LOG.error( E.GROUP_NOE);
3448       throw new GrouperException(E.GROUP_NOE);
3449     }
3450     return val;
3451   } // public String getExtension()
3452  
3453   /**
3454    * Get immediate members of this group.  
3455    * 
3456    * An immediate member is directly assigned to a group.
3457    * A composite group has no immediate members.  Note that a 
3458    * member can have 0 to 1 immediate memberships
3459    * to a single group, and 0 to many effective memberships to a group.
3460    * A group can have potentially unlimited effective 
3461    * memberships
3462    * 
3463    * <pre class="eg">
3464    * Set immediates = g.getImmediateMembers();
3465    * </pre>
3466    * @return  A set of {@link Member} objects.
3467    * @throws  GrouperException
3468    */
3469   public Set<Member> getImmediateMembers() 
3470     throws  GrouperException
3471   {
3472     try {
3473       return this.getImmediateMembers(getDefaultList());
3474     }
3475     catch (SchemaException eS) {
3476       // If we don't have "members" we have serious issues
3477       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
3478       LOG.fatal(msg);
3479       throw new GrouperException(msg, eS);
3480     }
3481   } // public Set getImmediateMembers()
3482 
3483   /**
3484    * Get immediate members of this group.  
3485    * 
3486    * An immediate member is directly assigned to a group.
3487    * A composite group has no immediate members.  Note that a 
3488    * member can have 0 to 1 immediate memberships
3489    * to a single group, and 0 to many effective memberships to a group.
3490    * A group can have potentially unlimited effective 
3491    * memberships
3492    * 
3493    * <pre class="eg">
3494    * Set immediates = g.getImmediateMembers(f);
3495    * </pre>
3496    * @param   f Get members in this list field.
3497    * @return  A set of {@link Member} objects.
3498    * @throws  SchemaException
3499    */
3500   public Set<Member> getImmediateMembers(Field f) 
3501     throws  SchemaException {
3502     return getImmediateMembers(f, null);
3503   }
3504 
3505   /**
3506    * Get immediate members of this group.  
3507    * 
3508    * An immediate member is directly assigned to a group.
3509    * A composite group has no immediate members.  Note that a 
3510    * member can have 0 to 1 immediate memberships
3511    * to a single group, and 0 to many effective memberships to a group.
3512    * A group can have potentially unlimited effective 
3513    * memberships
3514    * 
3515    * <pre class="eg">
3516    * Set immediates = g.getImmediateMembers(f);
3517    * </pre>
3518    * @param   f Get members in this list field.
3519    * @param queryOptions 
3520    * @return  A set of {@link Member} objects.
3521    * @throws  SchemaException
3522    */
3523   public Set<Member> getImmediateMembers(Field f, QueryOptions queryOptions) 
3524     throws  SchemaException {
3525     return getImmediateMembers(f, null, queryOptions);
3526   }
3527 
3528     
3529     
3530   /**
3531    * Get immediate members of this group.  
3532    * 
3533    * An immediate member is directly assigned to a group.
3534    * A composite group has no immediate members.  Note that a 
3535    * member can have 0 to 1 immediate memberships
3536    * to a single group, and 0 to many effective memberships to a group.
3537    * A group can have potentially unlimited effective 
3538    * memberships
3539    * 
3540    * <pre class="eg">
3541    * Set immediates = g.getImmediateMembers(f);
3542    * </pre>
3543    * @param   f Get members in this list field.
3544    * @param sources to search in or null if all
3545    * @param queryOptions 
3546    * @return  A set of {@link Member} objects.
3547    * @throws  SchemaException
3548    */
3549   public Set<Member> getImmediateMembers(Field f, Set<Source> sources, QueryOptions queryOptions) 
3550     throws  SchemaException {
3551     return MemberFinder.internal_findMembersByType(
3552       GrouperSession.staticGrouperSession(), this, f, MembershipType.IMMEDIATE.getTypeString(), sources, queryOptions,
3553       null, null, null
3554     );
3555   }
3556 
3557   /**
3558    * more efficient? way to see if there are any members
3559    * you need read on the group to call this method
3560    * @return true if has members, false if not
3561    */
3562   public boolean isHasMembers() {
3563     
3564     //just get one member
3565     Set<Member> members = this.getImmediateMembers(Group.getDefaultList(), null, 
3566         QueryOptions.create(null, null, 1, 1));
3567     
3568     return GrouperUtil.length(members) > 0;
3569     
3570   }
3571   
3572   /**
3573    * Get immediate members of this group.  
3574    * 
3575    * An immediate member is directly assigned to a group.
3576    * A composite group has no immediate members.  Note that a 
3577    * member can have 0 to 1 immediate memberships
3578    * to a single group, and 0 to many effective memberships to a group.
3579    * A group can have potentially unlimited effective 
3580    * memberships
3581    * 
3582    * <pre class="eg">
3583    * Set immediates = g.getImmediateMembers(f);
3584    * </pre>
3585    * @param   f Get members in this list field.
3586    * @param sources to search in or null if all
3587    * @param queryOptions 
3588    * @param memberSortStringEnum How to sort results or null for no sorting unless specified by queryOptions
3589    * @param memberSearchStringEnum Specify search string if searching for members in the group
3590    * @param memberSearchStringValue Search string value.
3591    * @return  A set of {@link Member} objects.
3592    * @throws  SchemaException
3593    */
3594   public Set<Member> getImmediateMembers(Field f, Set<Source> sources, QueryOptions queryOptions, 
3595       SortStringEnum memberSortStringEnum, SearchStringEnum memberSearchStringEnum, 
3596       String memberSearchStringValue) 
3597     throws  SchemaException {
3598     return MemberFinder.internal_findMembersByType(
3599       GrouperSession.staticGrouperSession(), this, f, MembershipType.IMMEDIATE.getTypeString(), sources, queryOptions,
3600       memberSortStringEnum, memberSearchStringEnum, memberSearchStringValue
3601     );
3602   }
3603 
3604   /**
3605    * Get immediate memberships of this group.  
3606    * 
3607    * An immediate member is directly assigned to a group.
3608    * A composite group has no immediate members.  Note that a 
3609    * member can have 0 to 1 immediate memberships
3610    * to a single group, and 0 to many effective memberships to a group.
3611    * A group can have potentially unlimited effective 
3612    * memberships
3613    * 
3614    * A membership is the object which represents a join of member
3615    * and group.  Has metadata like type and creator,
3616    * and, if an effective membership, the parent membership
3617    * 
3618    * <pre class="eg">
3619    * Set immediates = g.getImmediateMemberships();
3620    * </pre>
3621    * @return  A set of {@link Membership} objects.
3622    * @throws  GrouperException
3623    */
3624   public Set<Membership> getImmediateMemberships() 
3625     throws  GrouperException
3626   {
3627     try {
3628       return this.getImmediateMemberships(getDefaultList());
3629     }
3630     catch (SchemaException eS) {
3631       // If we don't have "members" we have serious issues
3632       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
3633       LOG.fatal(msg);
3634       throw new GrouperException(msg, eS);
3635     }
3636   } // public Set getImmediateMemberships()
3637 
3638   /**
3639    * An immediate member is directly assigned to a group.
3640    * A composite group has no immediate members.  Note that a 
3641    * member can have 0 to 1 immediate memberships
3642    * to a single group, and 0 to many effective memberships to a group.
3643    * A group can have potentially unlimited effective 
3644    * memberships
3645    * 
3646    * A membership is the object which represents a join of member
3647    * and group.  Has metadata like type and creator,
3648    * and, if an effective membership, the parent membership
3649    * 
3650    * <pre class="eg">
3651    * Set immediates = g.getImmediateMemberships(f);
3652    * </pre>
3653    * @param   f Get memberships in this list field.
3654    * @return  A set of {@link Membership} objects.
3655    * @throws  SchemaException
3656    */
3657   public Set<Membership> getImmediateMemberships(Field f) 
3658     throws  SchemaException
3659   {
3660     GrouperSession.validate(GrouperSession.staticGrouperSession());
3661     return MembershipFinder.internal_findAllByGroupOwnerAndFieldAndType(
3662       GrouperSession.staticGrouperSession(), this, f, MembershipType.IMMEDIATE.getTypeString()
3663     );
3664   } // public Set getImmediateMemberships(f)
3665 
3666   /**
3667    * Get members of this group.
3668    * <pre class="eg">
3669    * Set members = g.getMembers();
3670    * </pre>
3671    * @return  A set of {@link Member} objects.
3672    * @throws  GrouperException
3673    */
3674   public Set<Member> getMembers() 
3675     throws  GrouperException
3676   {
3677     try {
3678       return this.getMembers(getDefaultList());
3679     }
3680     catch (SchemaException eS) {
3681       // If we don't have "members" we have serious issues
3682       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
3683       LOG.fatal(msg);
3684       throw new GrouperException(msg, eS);
3685     }
3686   } // public Set getMembers()
3687 
3688   /**
3689    * Get members of this group.
3690    * <pre class="eg">
3691    * Set members = g.getMembers(f);
3692    * </pre>
3693    * @param   f Get members in this list field.
3694    * @return  A set of {@link Member} objects.
3695    * @throws  SchemaException
3696    */
3697   public Set<Member> getMembers(Field f) 
3698     throws  SchemaException {
3699     return this.getMembers(f, null);
3700   } 
3701 
3702   /**
3703    * Get members of this group.
3704    * <pre class="eg">
3705    * Set members = g.getMembers(f);
3706    * </pre>
3707    * @param   f Get members in this list field.
3708    * @param queryOptions paging, sorting, count, etc
3709    * @return  A set of {@link Member} objects.
3710    * @throws  SchemaException
3711    */
3712   public Set<Member> getMembers(Field f, QueryOptions queryOptions)
3713     throws  SchemaException {
3714     return getMembers(f, null, queryOptions);
3715   } 
3716 
3717   /**
3718    * Get members of this group.
3719    * <pre class="eg">
3720    * Set members = g.getMembers(f);
3721    * </pre>
3722    * @param   f Get members in this list field.to get members from, or null for all
3723    * @param sources to get members from, or null for all
3724    * @param queryOptions paging, sorting, count, etc
3725    * @return  A set of {@link Member} objects.
3726    * @throws  SchemaException
3727    */
3728   public Set<Member> getMembers(Field f, Set<Source> sources, QueryOptions queryOptions)
3729     throws  SchemaException {
3730     return MembershipFinder.findMembers(this, f, sources, queryOptions);
3731   } 
3732 
3733   /**
3734    * Get memberships of this group.
3735    * 
3736    * A membership is the object which represents a join of member
3737    * and group.  Has metadata like type and creator,
3738    * and, if an effective membership, the parent membership
3739    * 
3740    * <pre class="eg">
3741    * Set memberships = g.getMemberships();
3742    * </pre>
3743    * @return  A set of {@link Membership} objects.
3744    * @throws  GrouperException
3745    */
3746   public Set<Membership> getMemberships() 
3747     throws  GrouperException
3748   {
3749     try {
3750       return this.getMemberships(getDefaultList());
3751     }
3752     catch (SchemaException eS) {
3753       // If we don't have "members" we have serious issues
3754       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
3755       LOG.fatal(msg);
3756       throw new GrouperException(msg, eS);
3757     }
3758   } // public Set getMemberships()
3759 
3760   /**
3761    * Get memberships of this group.
3762    * 
3763    * A membership is the object which represents a join of member
3764    * and group.  Has metadata like type and creator,
3765    * and, if an effective membership, the parent membership
3766    * 
3767    * <pre class="eg">
3768    * Set memberships = g.getMemberships(f);
3769    * </pre>
3770    * @param   f Get memberships in this list field.
3771    * @return  A set of {@link Membership} objects.
3772    * @throws  SchemaException
3773    */
3774   public Set<Membership> getMemberships(Field f) 
3775     throws  SchemaException
3776   {
3777     return new LinkedHashSet<Membership>( 
3778       PrivilegeHelper.canViewMemberships( 
3779         GrouperSession.staticGrouperSession(), GrouperDAOFactory.getFactory()
3780           .getMembership().findAllByGroupOwnerAndField(this.getUuid(), f, true)
3781       )
3782     );
3783   } // public Set getMemberships(f)
3784 
3785   /**
3786    * Get memberships of this group, for a certain collection of members
3787    * 
3788    * A membership is the object which represents a join of member
3789    * and group.  Has metadata like type and creator,
3790    * and, if an effective membership, the parent membership
3791    * 
3792    * <pre class="eg">
3793    * Set memberships = g.getMemberships(f);
3794    * </pre>
3795    * @param   f Get memberships in this list field.
3796    * @param members 
3797    * @return  A set of {@link Membership} objects.
3798    * @throws  SchemaException
3799    */
3800   public Set<Membership> getMemberships(Field f, Collection<Member> members) 
3801     throws  SchemaException {
3802     return PrivilegeHelper.canViewMemberships( 
3803         GrouperSession.staticGrouperSession(), GrouperDAOFactory.getFactory().getMembership()
3804           .findAllByGroupOwnerAndFieldAndMembers(this.getUuid(), f, members, true)
3805       );
3806   }
3807 
3808   /**
3809    * Get memberships of this group, for a certain collection of members, must be enabled
3810    * 
3811    * A membership is the object which represents a join of member
3812    * and group.  Has metadata like type and creator,
3813    * and, if an effective membership, the parent membership
3814    * 
3815    * <pre class="eg">
3816    * Set memberships = g.getMemberships(f);
3817    * </pre>
3818    * @param   f Get memberships in this list field.
3819    * @param members 
3820    * @return  A set of {@link Membership} objects.
3821    * @throws  SchemaException
3822    */
3823   public Set<Membership> getImmediateMemberships(Field f, Collection<Member> members) 
3824     throws  SchemaException {
3825     return getImmediateMemberships(f, members, true);
3826   }
3827 
3828   /**
3829    * Get memberships of this group, for a certain collection of members
3830    * 
3831    * A membership is the object which represents a join of member
3832    * and group.  Has metadata like type and creator,
3833    * and, if an effective membership, the parent membership
3834    * 
3835    * <pre class="eg">
3836    * Set memberships = g.getMemberships(f);
3837    * </pre>
3838    * @param   f Get memberships in this list field.
3839    * @param members 
3840    * @param enabledOnly
3841    * @return  A set of {@link Membership} objects.
3842    * @throws  SchemaException
3843    */
3844   public Set<Membership> getImmediateMemberships(Field f, Collection<Member> members, boolean enabledOnly) 
3845     throws  SchemaException {
3846     return PrivilegeHelper.canViewMemberships( 
3847         GrouperSession.staticGrouperSession(), GrouperDAOFactory.getFactory().getMembership()
3848           .findAllByGroupOwnerAndFieldAndMembersAndType(this.getUuid(), f, members, 
3849               MembershipType.IMMEDIATE.getTypeString(), enabledOnly)
3850       );
3851   }
3852 
3853   /**
3854    * Get membership of this group, for a certain member
3855    * 
3856    * A membership is the object which represents a join of member
3857    * and group.  Has metadata like type and creator,
3858    * and, if an effective membership, the parent membership
3859    * 
3860    * <pre class="eg">
3861    * Membership membership = g.getMembership(f, m, true);
3862    * </pre>
3863    * @param   f Get memberships in this list field.
3864    * @param subject
3865    * @param enabledOnly 
3866    * @param exceptionIfNotFound true if MembershipNotFoundException should be thrown if not found, otherwise null
3867    * @return A set of {@link Membership} objects.
3868    * @throws SchemaException
3869    * @throws MembershipNotFoundException if none found and exceptionIfNotFound
3870    */
3871   public Membership getImmediateMembership(Field f, Subject subject, boolean enabledOnly, boolean exceptionIfNotFound) 
3872       throws  SchemaException, MembershipNotFoundException {
3873     Member member = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subject, true);
3874     return getImmediateMembership(f, member, enabledOnly, exceptionIfNotFound);
3875   }
3876 
3877   /**
3878    * Get membership of this group, for a certain member
3879    * 
3880    * A membership is the object which represents a join of member
3881    * and group.  Has metadata like type and creator,
3882    * and, if an effective membership, the parent membership
3883    * 
3884    * <pre class="eg">
3885    * Membership membership = g.getMembership(f, m, true);
3886    * </pre>
3887    * @param   f Get memberships in this list field.
3888    * @param member
3889    * @param enabledOnly 
3890    * @param exceptionIfNotFound true if MembershipNotFoundException should be thrown if not found, otherwise null
3891    * @return A set of {@link Membership} objects.
3892    * @throws SchemaException
3893    * @throws MembershipNotFoundException if none found and exceptionIfNotFound
3894    */
3895   public Membership getImmediateMembership(Field f, Member member, boolean enabledOnly, boolean exceptionIfNotFound) 
3896       throws  SchemaException, MembershipNotFoundException {
3897     
3898     if (member == null) {
3899       throw new RuntimeException("You need to pass a member here");
3900     }
3901     
3902     Set<Membership> memberships = this.getImmediateMemberships(f, GrouperUtil.toSet(member), enabledOnly);
3903     if (memberships.size() == 0) {
3904       if (exceptionIfNotFound) {
3905         throw new MembershipNotFoundException("Cant find memberships for group: " + this + ", and member: " + member
3906             + ", and field: " + f);
3907       }
3908       return null;
3909     }
3910     if (memberships.size() > 1) {
3911       throw new RuntimeException("There are more than one memberships: " + memberships.size()
3912           + " for group: " + this + ", and member: " + member
3913             + ", and field: " + f);
3914     }
3915     return memberships.iterator().next();
3916   }
3917 
3918   /**
3919    * Get memberships of this group, for a certain collection of members
3920    * 
3921    * A membership is the object which represents a join of member
3922    * and group.  Has metadata like type and creator,
3923    * and, if an effective membership, the parent membership
3924    * 
3925    * <pre class="eg">
3926    * Set memberships = g.getMemberships(f);
3927    * </pre>
3928    * @param   f Get memberships in this list field.
3929    * @param members 
3930    * @return  A set of {@link Membership} objects.
3931    * @throws  SchemaException
3932    */
3933   public Set<Membership> getEffectiveMemberships(Field f, Collection<Member> members) 
3934     throws  SchemaException {
3935     return PrivilegeHelper.canViewMemberships( 
3936         GrouperSession.staticGrouperSession(), GrouperDAOFactory.getFactory().getMembership()
3937           .findAllByGroupOwnerAndFieldAndMembersAndType(this.getUuid(), f, members, "effective", true)
3938       );
3939   }
3940 
3941   /**
3942    * Get memberships of this group, for a certain collection of members
3943    * 
3944    * A membership is the object which represents a join of member
3945    * and group.  Has metadata like type and creator,
3946    * and, if an effective membership, the parent membership
3947    * 
3948    * <pre class="eg">
3949    * Set memberships = g.getMemberships(f);
3950    * </pre>
3951    * @param members 
3952    * @return  A set of {@link Membership} objects.
3953    * @throws  SchemaException
3954    */
3955   public Set<Membership> getCompositeMemberships(Collection<Member> members) 
3956     throws  SchemaException {
3957     return PrivilegeHelper.canViewMemberships( 
3958         GrouperSession.staticGrouperSession(), GrouperDAOFactory.getFactory().getMembership()
3959           .findAllByGroupOwnerAndCompositeAndMembers(this.getUuid(), members, true)
3960       );
3961   }
3962 
3963   /**
3964    * Get subject that last modified this group.
3965    * <pre class="eg">
3966    * try {
3967    *   Subject modifier = g.getModifySubject();
3968    * }
3969    * catch (SubjectNotFoundException e) {
3970    *   // Couldn't find subject
3971    * }
3972    * </pre>
3973    * @return  {@link Subject} that last modified this group.
3974    * @throws  SubjectNotFoundException
3975    */
3976   public Subject getModifySubject() 
3977     throws SubjectNotFoundException
3978   {
3979     if ( this.subjectCache.containsKey(KEY_MODIFIER) ) {
3980       return this.subjectCache.get(KEY_MODIFIER);
3981     }
3982     if ( this.getModifierUuid() == null ) {
3983       throw new SubjectNotFoundException("group has not been modified");
3984     }
3985     try {
3986       // when called from "GrouperSubject" there is no attached session
3987       Member _m = GrouperDAOFactory.getFactory().getMember().findByUuid( this.getModifierUuid(), true );
3988       this.subjectCache.put(
3989         KEY_MODIFIER, SubjectFinder.findByIdAndSource( _m.getSubjectId(), _m.getSubjectSourceId(), true )
3990       );
3991       return this.subjectCache.get(KEY_MODIFIER);
3992     }
3993     catch (MemberNotFoundException eMNF) {
3994       throw new SubjectNotFoundException( eMNF.getMessage(), eMNF );
3995     }
3996     catch (SourceUnavailableException eSU) {
3997       throw new SubjectNotFoundException( eSU.getMessage(), eSU );
3998     }
3999     catch (SubjectNotUniqueException eSNU) {
4000       throw new SubjectNotFoundException( eSNU.getMessage(), eSNU );
4001     }  
4002   } // public Subject getModifySubject()
4003   
4004   /**
4005    * Get last modified time for this group.
4006    * <pre class="eg">
4007    * Date modified = g.getModifyTime();
4008    * </pre>
4009    * @return  {@link Date} that this group was last modified.
4010    */
4011   public Date getModifyTime() {
4012     return new Date( this.getModifyTimeLong() );
4013   }
4014 
4015   /**
4016    * Get group name.
4017    * <pre class="eg">
4018    * String name = g.getName();
4019    * </pre>
4020    * @return  Group name.
4021    * @throws  GrouperException
4022    */
4023   public String getName() 
4024     throws  GrouperException
4025   {
4026     // We don't validate privs here because if one has retrieved a group then one
4027     // has at least VIEW.
4028     String val = this.name;
4029     if ( val == null || GrouperConfig.EMPTY_STRING.equals(val) ) {
4030       //  A group without this attribute is VERY faulty
4031       LOG.error( E.GROUP_NON);
4032       throw new GrouperException(E.GROUP_NON);
4033     }
4034     return val;
4035   } // public String getName()
4036 
4037   /**
4038    * Get subjects with the OPTIN privilege on this group.
4039    * <pre class="eg">
4040    * Set optins = g.getOptins();
4041    * </pre>
4042    * @return  Set of subjects with OPTIN
4043    * @throws  GrouperException
4044    */
4045   public Set<Subject> getOptins() 
4046     throws  GrouperException
4047   {
4048     return GrouperSession.staticGrouperSession().getAccessResolver().getSubjectsWithPrivilege(this, AccessPrivilege.OPTIN);
4049   } 
4050 
4051   /**
4052    * Get subjects with the OPTOUT privilege on this group.
4053    * <pre class="eg">
4054    * Set admins = g.getOptouts();
4055    * </pre>
4056    * @return  Set of subjects with OPTOUT
4057    * @throws  GrouperException
4058    */
4059   public Set<Subject> getOptouts() 
4060     throws  GrouperException
4061   {
4062     return GrouperSession.staticGrouperSession().getAccessResolver().getSubjectsWithPrivilege(this, AccessPrivilege.OPTOUT);
4063   } 
4064   
4065   /**
4066    * Get subjects with the GROUP_ATTR_READ privilege on this group.
4067    * <pre class="eg">
4068    * Set subjects = g.getGroupAttrReaders();
4069    * </pre>
4070    * @return  Set of subjects with GROUP_ATTR_READ
4071    * @throws  GrouperException
4072    */
4073   public Set<Subject> getGroupAttrReaders() 
4074     throws  GrouperException
4075   {
4076     return GrouperSession.staticGrouperSession().getAccessResolver().getSubjectsWithPrivilege(this, AccessPrivilege.GROUP_ATTR_READ);
4077   } 
4078   
4079   /**
4080    * Get subjects with the GROUP_ATTR_UPDATE privilege on this group.
4081    * <pre class="eg">
4082    * Set subjects = g.getGroupAttrUpdaters();
4083    * </pre>
4084    * @return  Set of subjects with GROUP_ATTR_UPDATE
4085    * @throws  GrouperException
4086    */
4087   public Set<Subject> getGroupAttrUpdaters() 
4088     throws  GrouperException
4089   {
4090     return GrouperSession.staticGrouperSession().getAccessResolver().getSubjectsWithPrivilege(this, AccessPrivilege.GROUP_ATTR_UPDATE);
4091   } 
4092 
4093   /**
4094    * get the name of the parent stem
4095    * @return the name of the parent stem
4096    */
4097   public String getParentStemName() {
4098     return GrouperUtil.parentStemNameFromName(this.getName(), false);
4099   } 
4100 
4101   /**
4102    * Get parent stem.
4103    * <pre class="eg">
4104    * Stem parent = g.getParentStem();
4105    * </pre>
4106    * @return  Parent {@link Stem}.
4107    * @throws IllegalStateException 
4108    */
4109   public Stem getParentStem() 
4110     throws  IllegalStateException
4111   {
4112     final String uuid = this.getParentUuid();
4113     if (uuid == null) {
4114       throw new IllegalStateException("group has no parent stem");
4115     }
4116     try {
4117       //try in transaction
4118       Stem parent = GrouperDAOFactory.getFactory().getStem().findByUuid(uuid, false) ;
4119       
4120       if (parent == null) {
4121         //try out of transaction
4122         parent = (Stem)GrouperTransaction.callbackGrouperTransaction(GrouperTransactionType.NONE, new GrouperTransactionHandler() {
4123           
4124           public Object callback(GrouperTransaction grouperTransaction)
4125               throws GrouperDAOException {
4126             return GrouperDAOFactory.getFactory().getStem().findByUuid(uuid, true);
4127           }
4128         });
4129         
4130         if (parent != null) {
4131           
4132           //wait a bit for it to be available in this transaction
4133           GrouperUtil.sleep(2000);
4134           
4135         }
4136       }
4137       return parent;
4138     }
4139     catch (StemNotFoundException eShouldNeverHappen) {
4140       throw new IllegalStateException( 
4141         "this should never happen: group has no parent stem: " + eShouldNeverHappen.getMessage(), 
4142         eShouldNeverHappen 
4143       );
4144     }
4145   } // public Stem getParentStem()
4146 
4147   /**
4148    * Get privileges that the specified subject has on this group.
4149    * <pre class="eg">
4150    * Set privs = g.getPrivs(subj);
4151    * </pre>
4152    * @param   subj  Get privileges for this subject.
4153    * @return  Set of {@link AccessPrivilege} objects.
4154    */
4155   public Set<AccessPrivilege> getPrivs(Subject subj) {
4156     return GrouperSession.staticGrouperSession().getAccessResolver().getPrivileges(this, subj);
4157   } 
4158 
4159 
4160   /**
4161    * Get subjects with the READ privilege on this group.
4162    * <pre class="eg">
4163    * Set readers = g.getReaders();
4164    * </pre>
4165    * @return  Set of subjects with READ
4166    * @throws  GrouperException
4167    */
4168   public Set<Subject> getReaders() 
4169     throws  GrouperException
4170   {
4171     return GrouperSession.staticGrouperSession().getAccessResolver().getSubjectsWithPrivilege(this, AccessPrivilege.READ);
4172   } 
4173 
4174   /**
4175    * Get removable group types for this group.
4176    * <pre class="eg">
4177    * Set types = g.getRemovableTypes();
4178    * </pre>
4179    * @return  Set of removable group types.
4180    * @since   1.0
4181    * @deprecated
4182    */
4183   public Set<GroupType> getRemovableTypes() {
4184     Set<GroupType> types = new LinkedHashSet<GroupType>();
4185     // Must have ADMIN to remove types.
4186     if (PrivilegeHelper.canAdmin(GrouperSession.staticGrouperSession(), this, GrouperSession.staticGrouperSession().getSubject())) {
4187       GroupType t;
4188       Iterator  iter  = this.getTypes().iterator();
4189       while (iter.hasNext()) {
4190         t = (GroupType) iter.next();
4191         types.add(t);
4192       }
4193     }
4194     return types;
4195   } // public Set getRemovableTypes()
4196 
4197   /**
4198    * Get group types for this group (secure method).
4199    * <pre class="eg">
4200    * Set types = g.getTypes();
4201    * </pre>
4202    * @return  Set of group types.
4203    * @deprecated
4204    */
4205   public Set<GroupType> getTypes() {
4206     return getTypes(true);
4207   }
4208 
4209   
4210   /**
4211    * Get group types for this group.
4212    * <pre class="eg">
4213    * Set types = g.getTypes(true);
4214    * </pre>
4215    * @param checkSecurity 
4216    * @return  Set of group types.
4217    * @deprecated
4218    */
4219   public Set<GroupType> getTypes(boolean checkSecurity) {
4220     this.internal_getGroupTypeAssignments();
4221     
4222     Set<GroupType> results = new LinkedHashSet<GroupType>();
4223     
4224     for (String groupTypeName : this.typeAssignments.keySet()) {
4225       AttributeAssign legacyGroupTypeAssignment = this.typeAssignments.get(groupTypeName);
4226       if (!checkSecurity || PrivilegeHelper.canViewAttributeAssign(GrouperSession.staticGrouperSession(), legacyGroupTypeAssignment, true)) {
4227         GroupType groupType = GroupType.internal_getGroupType(legacyGroupTypeAssignment.getAttributeDefName(), true);
4228         results.add(groupType);
4229       }
4230     }
4231     
4232     return results;
4233   }
4234 
4235   /**
4236    * get types in db
4237    * @return types
4238    * @deprecated
4239    */
4240   public Set<GroupType> getTypesDb() {
4241     if (this.types == null) {
4242       this.typeAssignments = GrouperDAOFactory.getFactory().getAttributeAssign().findLegacyGroupTypeAssignmentsByGroupId(this.getUuid());
4243       Map<String, AttributeDefName> results = new LinkedHashMap<String, AttributeDefName>();
4244       for (String groupTypeName : this.typeAssignments.keySet()) {
4245         AttributeAssign legacyGroupTypeAssignment = this.typeAssignments.get(groupTypeName);
4246         results.put(groupTypeName, legacyGroupTypeAssignment.getAttributeDefName());
4247       }
4248       
4249       this.types = new LinkedHashMap<String, AttributeDefName>(results);
4250     }
4251     
4252     Set<GroupType> results = new LinkedHashSet<GroupType>();
4253     
4254     for (AttributeDefName legacyGroupType : this.types.values()) {
4255       GroupType groupType = GroupType.internal_getGroupType(legacyGroupType, true);
4256       results.add(groupType);
4257     }
4258     
4259     return results;
4260   }
4261   
4262   /**
4263    * Get subjects with the UPDATE privilege on this group.
4264    * <pre class="eg">
4265    * Set updaters = g.getUpdaters();
4266    * </pre>
4267    * @return  Set of subjects with UPDATE
4268    * @throws  GrouperException
4269    */
4270   public Set<Subject> getUpdaters() 
4271     throws  GrouperException
4272   {
4273     return GrouperSession.staticGrouperSession().getAccessResolver().getSubjectsWithPrivilege(this, AccessPrivilege.UPDATE);
4274   } 
4275 
4276   /**
4277    * Get subjects with the VIEW privilege on this group.
4278    * <pre class="eg">
4279    * Set viewers = g.getViewers();
4280    * </pre>
4281    * @return  Set of subjects with VIEW
4282    * @throws  GrouperException
4283    */
4284   public Set<Subject> getViewers() 
4285     throws  GrouperException
4286   {
4287     return GrouperSession.staticGrouperSession().getAccessResolver().getSubjectsWithPrivilege(this, AccessPrivilege.VIEW);
4288   } 
4289 
4290   /**
4291    * Grant privilege to a subject on this group.  This
4292    * will throw an exception if the privilege already exists
4293    * <pre class="eg">
4294    * try {
4295    *   g.grantPriv(subj, AccessPrivilege.ADMIN);
4296    * }
4297    * catch (GrantPrivilegeException e0) {
4298    *   // Not privileged to grant this privilege
4299    * }
4300    * catch (InsufficientPrivilegeException e1) {
4301    *   // Unable to grant this privilege
4302    * }
4303    * </pre>
4304    * @param   subj  Grant privilege to this subject.
4305    * @param   priv  Grant this privilege.
4306    * @throws  GrantPrivilegeException
4307    * @throws  InsufficientPrivilegeException
4308    * @throws  SchemaException
4309    */
4310   public void grantPriv(Subject subj, Privilege priv)
4311     throws  GrantPrivilegeException,
4312             InsufficientPrivilegeException,
4313             SchemaException {
4314     
4315     grantPriv(subj, priv, true);
4316     
4317   }
4318   
4319   /**
4320    * Grant privilege to a subject on this group.
4321    * <pre class="eg">
4322    * try {
4323    *   g.grantPriv(subj, AccessPrivilege.ADMIN);
4324    * }
4325    * catch (GrantPrivilegeException e0) {
4326    *   // Not privileged to grant this privilege
4327    * }
4328    * catch (InsufficientPrivilegeException e1) {
4329    *   // Unable to grant this privilege
4330    * }
4331    * </pre>
4332    * @param   subj  Grant privilege to this subject.
4333    * @param   priv  Grant this privilege.
4334    * @param exceptionIfAlreadyMember if false, and subject is already a member,
4335    * then dont throw a MemberAddException if the member is already in the group
4336    * @throws  GrantPrivilegeException
4337    * @throws  InsufficientPrivilegeException
4338    * @throws  SchemaException
4339    * @return false if it already existed, true if it didnt already exist
4340    */
4341   public boolean grantPriv(final Subject subj, final Privilege priv, final boolean exceptionIfAlreadyMember)
4342     throws  GrantPrivilegeException,
4343             InsufficientPrivilegeException,
4344             SchemaException {
4345     return internal_grantPriv(subj, priv, exceptionIfAlreadyMember, null);
4346   }
4347   
4348   /**
4349    * Grant privilege to a subject on this group.
4350    * <pre class="eg">
4351    * try {
4352    *   g.grantPriv(subj, AccessPrivilege.ADMIN);
4353    * }
4354    * catch (GrantPrivilegeException e0) {
4355    *   // Not privileged to grant this privilege
4356    * }
4357    * catch (InsufficientPrivilegeException e1) {
4358    *   // Unable to grant this privilege
4359    * }
4360    * </pre>
4361    * @param   subj  Grant privilege to this subject.
4362    * @param   priv  Grant this privilege.
4363    * @param exceptionIfAlreadyMember if false, and subject is already a member,
4364    * then dont throw a MemberAddException if the member is already in the group
4365    * @param uuid
4366    * @throws  GrantPrivilegeException
4367    * @throws  InsufficientPrivilegeException
4368    * @throws  SchemaException
4369    * @return false if it already existed, true if it didnt already exist
4370    */
4371   public boolean internal_grantPriv(final Subject subj, final Privilege priv, final boolean exceptionIfAlreadyMember, final String uuid)
4372     throws  GrantPrivilegeException,
4373             InsufficientPrivilegeException,
4374             SchemaException {
4375     final StopWatch sw = new StopWatch();
4376     sw.start();
4377 
4378     final String errorMessageSuffix = ", group name: " + this.name 
4379       + ", subject: " + GrouperUtil.subjectToString(subj) + ", privilege: " + (priv == null ? null : priv.getName());
4380 
4381     return (Boolean)HibernateSession.callbackHibernateSession(
4382       GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
4383       new HibernateHandler() {
4384 
4385         public Object callback(HibernateHandlerBean hibernateHandlerBean)
4386             throws GrouperDAOException {
4387 
4388           boolean assignedPrivilege = false;
4389           try {
4390             
4391             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
4392 
4393             GrouperSession.staticGrouperSession().getAccessResolver().grantPrivilege(Group.this, subj, priv, uuid);
4394             assignedPrivilege = true;
4395 
4396             RulesPrivilegeBeanivilegeBean.html#RulesPrivilegeBean">RulesPrivilegeBean rulesPrivilegeBean = new RulesPrivilegeBean(Group.this, subj, priv);
4397             
4398             //fire rules related to subject assign in folder
4399             RuleEngine.fireRule(RuleCheckType.subjectAssignInStem, rulesPrivilegeBean);
4400 
4401             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
4402               
4403               Member member = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subj, false);
4404               
4405               AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.PRIVILEGE_GROUP_ADD, "privilegeName", 
4406                   priv.getName(),  "memberId",  member.getUuid(),
4407                       "privilegeType", "access", 
4408                       "groupId", Group.this.getUuid(), "groupName", Group.this.getName());
4409                       
4410               auditEntry.setDescription("Added privilege: group: " + Group.this.getName()
4411                   + ", subject: " + subj.getSource().getId() + "." + subj.getId() + ", privilege: "
4412                   + priv.getName());
4413               auditEntry.saveOrUpdate(true);
4414             }
4415 
4416           } catch (UnableToPerformAlreadyExistsException eUTP) {
4417             if (exceptionIfAlreadyMember) {
4418               throw new GrantPrivilegeAlreadyExistsException(eUTP.getMessage() + errorMessageSuffix, eUTP);
4419             }
4420           } catch (UnableToPerformException eUTP) {
4421             throw new GrantPrivilegeException( eUTP.getMessage() + errorMessageSuffix, eUTP );
4422           }
4423           sw.stop();
4424           if (assignedPrivilege) {
4425             EVENT_LOG.groupGrantPriv(GrouperSession.staticGrouperSession(), Group.this.getName(), subj, priv, sw);
4426           }
4427           return assignedPrivilege;
4428         }
4429       });
4430   } 
4431   
4432   /**
4433    * see if the subject has a privilege
4434    * @param subject
4435    * @param privilegeOrListName
4436    * @return true if has privilege
4437    */
4438   public boolean hasPrivilege(Subject subject, String privilegeOrListName) {
4439     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.ADMIN.getName()) 
4440         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.ADMIN.getListName())) {
4441       return this.hasAdmin(subject);
4442     }
4443     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.READ.getName()) 
4444         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.READ.getListName())) {
4445       return this.hasRead(subject);
4446     }
4447     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.VIEW.getName()) 
4448         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.VIEW.getListName())) {
4449       return this.hasView(subject);
4450     }
4451     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.UPDATE.getName()) 
4452         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.UPDATE.getListName())) {
4453       return this.hasUpdate(subject);
4454     }
4455     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.GROUP_ATTR_READ.getName()) 
4456         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.GROUP_ATTR_READ.getListName())) {
4457       return this.hasGroupAttrRead(subject);
4458     }
4459     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.GROUP_ATTR_UPDATE.getName()) 
4460         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.GROUP_ATTR_UPDATE.getListName())) {
4461       return this.hasGroupAttrUpdate(subject);
4462     }
4463     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.OPTIN.getName()) 
4464         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.OPTIN.getListName())) {
4465       return this.hasOptin(subject);
4466     }
4467     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.OPTOUT.getName()) 
4468         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.OPTOUT.getListName())) {
4469       return this.hasOptout(subject);
4470     }
4471     throw new RuntimeException("Cant find privilege: '" + privilegeOrListName + "'");
4472 
4473   }
4474   
4475   /**
4476    * Check whether the subject has ADMIN on this group.
4477    * <pre class="eg">
4478    * if (g.hasAdmin(subj)) {
4479    *   // Has ADMIN
4480    * }
4481    * else {
4482    *   // Does not have ADMIN
4483    * }
4484    * </pre>
4485    * @param   subj  Check this subject.
4486    * @return  Boolean true if subject has ADMIN.
4487    */
4488   public boolean hasAdmin(Subject subj) {
4489     AccessResolver accessResolver = GrouperSession.staticGrouperSession().getAccessResolver();
4490     return accessResolver.hasPrivilege(this, subj, AccessPrivilege.ADMIN);
4491   } 
4492   
4493   /**
4494    * Check whether the subject has GROUP_ATTR_READ on this group.
4495    * <pre class="eg">
4496    * if (g.hasGroupAttrRead(subj)) {
4497    *   // Has GROUP_ATTR_READ
4498    * }
4499    * else {
4500    *   // Does not have GROUP_ATTR_READ
4501    * }
4502    * </pre>
4503    * @param   subj  Check this subject.
4504    * @return  Boolean true if subject has GROUP_ATTR_READ.
4505    */
4506   public boolean hasGroupAttrRead(Subject subj) {
4507     AccessResolver accessResolver = GrouperSession.staticGrouperSession().getAccessResolver();
4508     return accessResolver.hasPrivilege(this, subj, AccessPrivilege.GROUP_ATTR_READ);
4509   } 
4510   
4511   /**
4512    * Check whether the subject has GROUP_ATTR_UPDATE on this group.
4513    * <pre class="eg">
4514    * if (g.hasGroupAttrUpdate(subj)) {
4515    *   // Has GROUP_ATTR_UPDATE
4516    * }
4517    * else {
4518    *   // Does not have GROUP_ATTR_UPDATE
4519    * }
4520    * </pre>
4521    * @param   subj  Check this subject.
4522    * @return  Boolean true if subject has GROUP_ATTR_UPDATE.
4523    */
4524   public boolean hasGroupAttrUpdate(Subject subj) {
4525     AccessResolver accessResolver = GrouperSession.staticGrouperSession().getAccessResolver();
4526     return accessResolver.hasPrivilege(this, subj, AccessPrivilege.GROUP_ATTR_UPDATE);
4527   } 
4528 
4529   /**
4530    * Does this {@link Group} have a {@link Composite} membership.
4531    * <pre class="eg">
4532    * if (g.hasComposite()) {
4533    *   // this group has a composite membership
4534    * }
4535    * </pre>
4536    * @return  Boolean true if group has a composite membership.
4537    */
4538   public boolean hasComposite() {
4539     return null != GrouperDAOFactory.getFactory().getComposite().findAsOwner( this , false);
4540   } // public boolean hasComposite()
4541 
4542   /**
4543    * Does this {@link Group} have a {@link Composite} membership.
4544    * @see  Group#hasComposite()
4545    * @return  Boolean true if group has a composite membership.
4546    */
4547   public boolean isHasComposite() {
4548     return this.hasComposite();
4549   }
4550 
4551   /**
4552    * Check whether the subject is an effective member of this group.
4553    * 
4554    * An effective member has an indirect membership to a group
4555    * (e.g. in a group within a group).  All subjects in a
4556    * composite group are effective members (since the composite
4557    * group has two groups and a set operator and no other immediate
4558    * members).  Note that a member can have 0 to 1 immediate memberships
4559    * to a single group, and 0 to many effective memberships to a group.
4560    * 'group within a group' can be nested to any level so long as it does 
4561    * not become circular.  A group can have potentially unlimited effective 
4562    * memberships
4563    * 
4564    * <pre class="eg">
4565    * if (g.hasEffectiveMember(subj)) {
4566    *   // Subject is an effective member of this group
4567    * }
4568    * else {
4569    *   // Subject is not an effective member of this group
4570    * } 
4571    * </pre>
4572    * @param   subj  Check this subject.
4573    * @return  Boolean true if subject belongs to this group.
4574    * @throws  GrouperException
4575    */
4576   public boolean hasEffectiveMember(Subject subj) 
4577     throws  GrouperException
4578   {
4579     try {
4580       return this.hasEffectiveMember(subj, getDefaultList());
4581     } 
4582     catch (SchemaException eS) {
4583       // If we don't have "members" we have serious issues
4584       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
4585       LOG.fatal(msg);
4586       throw new GrouperException(msg, eS);
4587     }
4588   } // public boolean hasEffectiveMember(Subject subj)
4589 
4590   /**
4591    * Check whether the subject is an effective member of this group.  
4592    * 
4593    * An effective member has an indirect membership to a group
4594    * (e.g. in a group within a group).  All subjects in a
4595    * composite group are effective members (since the composite
4596    * group has two groups and a set operator and no other immediate
4597    * members).  Note that a member can have 0 to 1 immediate memberships
4598    * to a single group, and 0 to many effective memberships to a group.
4599    * 'group within a group' can be nested to any level so long as it does 
4600    * not become circular.  A group can have potentially unlimited effective 
4601    * memberships
4602    * 
4603    * <pre class="eg">
4604    * if (g.hasEffectiveMember(subj, f)) {
4605    *   // Subject is an effective member of this group
4606    * }
4607    * else {
4608    *   // Subject is not an effective member of this group
4609    * } 
4610    * </pre>
4611    * @param   subj  Check this subject.
4612    * @param   f     Check for membership in this list field.
4613    * @return  Boolean true if subject belongs to this group. 
4614    * @throws  SchemaException
4615    */
4616   public boolean hasEffectiveMember(Subject subj, Field f) 
4617     throws  SchemaException
4618   {
4619     boolean rv = false;
4620     Member m = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subj, true);
4621     rv = m.isEffectiveMember(this, f);
4622     return rv;
4623   } // public boolean hasEffectiveMember(subj, f)
4624 
4625   /**
4626    * Check whether the subject is an immediate member of this group.  
4627    * 
4628    * An immediate member is directly assigned to a group.
4629    * A composite group has no immediate members.  Note that a 
4630    * member can have 0 to 1 immediate memberships
4631    * to a single group, and 0 to many effective memberships to a group.
4632    * A group can have potentially unlimited effective 
4633    * memberships
4634    * 
4635    * <pre class="eg">
4636    * if (g.hasImmediateMember(subj)) {
4637    *   // Subject is an immediate member of this group
4638    * }
4639    * else {
4640    *   // Subject is not a immediate member of this group
4641    * } 
4642    * </pre>
4643    * @param   subj  Check this subject.
4644    * @return  Boolean true if subject belongs to this group.
4645    * @throws  GrouperException
4646    */
4647   public boolean hasImmediateMember(Subject subj) 
4648     throws  GrouperException
4649   {
4650     try {
4651       return this.hasImmediateMember(subj, getDefaultList());
4652     }
4653     catch (SchemaException eS) {
4654       // If we don't have "members" we have serious issues
4655       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
4656       LOG.fatal(msg);
4657       throw new GrouperException(msg, eS);
4658     }
4659   } // public boolean hasImmediateMember(subj)
4660 
4661   /**
4662    * Check whether the subject is an immediate member of this group.
4663    * 
4664    * An immediate member is directly assigned to a group.
4665    * A composite group has no immediate members.  Note that a 
4666    * member can have 0 to 1 immediate memberships
4667    * to a single group, and 0 to many effective memberships to a group.
4668    * A group can have potentially unlimited effective 
4669    * memberships
4670    * 
4671    * <pre class="eg">
4672    * if (g.hasImmediateMember(subj, f)) {
4673    *   // Subject is an immediate member of this group
4674    * }
4675    * else {
4676    *   // Subject is not a immediate member of this group
4677    * } 
4678    * </pre>
4679    * @param   subj  Check this subject.
4680    * @param   f     Check for membership in this list field.
4681    * @return  Boolean true if subject belongs to this group.
4682    * @throws  SchemaException
4683    */
4684   public boolean hasImmediateMember(Subject subj, Field f) 
4685     throws  SchemaException
4686   {
4687     boolean rv = false;
4688     Member m = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subj, true);
4689     rv = m.isImmediateMember(this, f);
4690     return rv;
4691   } // public boolean hasImmediateMember(subj, f)
4692 
4693   /**
4694    * Check whether the subject is a member of this group.
4695    * 
4696    * All immediate subjects, and effective members are members.  
4697    * No duplicates will be returned (e.g. if immediate and effective).
4698    * 
4699    * <pre class="eg">
4700    * if (g.hasMember(subj)) {
4701    *   // Subject is a member of this group
4702    * }
4703    * else {
4704    *   // Subject is not a member of this group
4705    * } 
4706    * </pre>
4707    * @param   subj  Check this subject.
4708    * @return  Boolean true if subject belongs to this group.
4709    * @throws  GrouperException
4710    */
4711   public boolean hasMember(Subject subj) 
4712     throws  GrouperException
4713   {
4714     try {
4715       return this.hasMember(subj, getDefaultList());
4716     }
4717     catch (SchemaException eShouldNeverHappen) {
4718       // If we don't have "members" we have serious issues
4719       String msg = "this should never happen: default group list not found: " + eShouldNeverHappen.getMessage();
4720       LOG.fatal(msg);
4721       throw new GrouperException(msg, eShouldNeverHappen);
4722     }
4723   } // public boolean hasMember(subj)
4724 
4725   /**
4726    * Check whether the subject is a member of this list on this group.
4727    * 
4728    * All immediate subjects, and effective members are members.  
4729    * No duplicates will be returned (e.g. if immediate and effective).
4730    * 
4731    * <pre class="eg">
4732    * if (g.hasMember(subj, f)) {
4733    *   // Subject is a member of this group
4734    * }
4735    * else {
4736    *   // Subject is not a member of this group
4737    * } 
4738    * </pre>
4739    * @param   subj  Check this subject.
4740    * @param   f     Is subject a member of this list {@link Field}.
4741    * @return  Boolean true if subject belongs to this group.
4742    * @throws  SchemaException
4743    */
4744   public boolean hasMember(Subject subj, Field f) 
4745     throws  SchemaException
4746   {
4747     boolean rv = false;
4748     Member m = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subj, true);
4749     rv = m.isMember(this, f);
4750     return rv;
4751   } // public boolean hasMember(subj, f)
4752 
4753   /**
4754    * Check whether the subject has OPTIN on this group.
4755    * <pre class="eg">
4756    * if (g.hasOptin(subj)) {
4757    *   // Has OPTIN
4758    * }
4759    * else {
4760    *   // Does not have OPTIN
4761    * }
4762    * </pre>
4763    * @param   subj  Check this subject.
4764    * @return  Boolean true if subject has OPTIN.
4765    */
4766   public boolean hasOptin(Subject subj) {
4767     return GrouperSession.staticGrouperSession().getAccessResolver().hasPrivilege(this, subj, AccessPrivilege.OPTIN);
4768   }
4769 
4770   /**
4771    * Check whether the subject has OPTOUT on this group.
4772    * <pre class="eg">
4773    * if (g.hasOptout(subj)) {
4774    *   // has OPTOUT
4775    * }
4776    * else {
4777    *   // Does not have OPTOUT
4778    * }
4779    * </pre>
4780    * @param   subj  Check this subject.
4781    * @return  Boolean true if subject has OPTOUT.
4782    */
4783   public boolean hasOptout(Subject subj) {
4784     return GrouperSession.staticGrouperSession().getAccessResolver().hasPrivilege(this, subj, AccessPrivilege.OPTOUT);
4785   } 
4786 
4787   /**
4788    * Check whether the subject has READ on this group.
4789    * <pre class="eg">
4790    * if (g.hasRead(subj)) {
4791    *   // Has READ
4792    * }
4793    * else {
4794    *   // Does not have READ
4795    * }
4796    * </pre>
4797    * @param   subj  Check this subject.
4798    * @return  Boolean true if subject has READ.
4799    */
4800   public boolean hasRead(Subject subj) {
4801     return GrouperSession.staticGrouperSession().getAccessResolver().hasPrivilege(this, subj, AccessPrivilege.READ);
4802   } 
4803 
4804   /**
4805    * Check whether group has the specified type.
4806    * <pre class="eg">
4807    * GroupType custom = GroupTypeFinder.find("custom type");
4808    * if (g.hasType(custom, true)) {
4809    *   // Group has type
4810    * }
4811    * </pre>
4812    * @param   type  The {@link GroupType} to check.
4813    * @param checkSecurity 
4814    * @return if has type
4815    */
4816   public boolean hasType(GroupType type, boolean checkSecurity) {
4817     return this.getTypes(checkSecurity).contains(type);
4818   }
4819   
4820   /**
4821    * Check whether group has the specified type.  This is a secure method
4822    * <pre class="eg">
4823    * GroupType custom = GroupTypeFinder.find("custom type");
4824    * if (g.hasType(custom)) {
4825    *   // Group has type
4826    * }
4827    * </pre>
4828    * @param   type  The {@link GroupType} to check.
4829    * @return if has type
4830    */
4831   public boolean hasType(GroupType type) {
4832     return this.hasType(type, true);
4833   } // public boolean hasType(type)
4834 
4835   /**
4836    * Check whether the subject has UPDATE on this group.
4837    * <pre class="eg">
4838    * if (g.hasUpdate(subj)) {
4839    *   // Has UPDATE
4840    * }
4841    * else {
4842    *   // Does not have UPDATE
4843    * }
4844    * </pre>
4845    * @param   subj  Check this subject.
4846    * @return  Boolean true if subject has UPDATE.
4847    */
4848   public boolean hasUpdate(Subject subj) {
4849     return GrouperSession.staticGrouperSession().getAccessResolver().hasPrivilege(this, subj, AccessPrivilege.UPDATE);
4850   } 
4851 
4852   /**
4853    * Check whether the subject has VIEW on this group.
4854    * <pre class="eg">
4855    * if (g.hasView(subj)) {
4856    *   // Has VIEW
4857    * }
4858    * else {
4859    *   // Does not have VIEW
4860    * }
4861    * </pre>
4862    * @param   subj  Check this member.
4863    * @return  Boolean true if subject has VIEW.
4864    */
4865   public boolean hasView(Subject subj) {
4866     return GrouperSession.staticGrouperSession().getAccessResolver().hasPrivilege(this, subj, AccessPrivilege.VIEW);
4867   } 
4868 
4869   /**
4870    * Is this {@link Group} a factor in a {@link Composite} membership.
4871    * <pre class="eg">
4872    * if (g.isComposite()) {
4873    *   // this group is a factor in one-or-more composite memberships.
4874    * }
4875    * </pre>
4876    * @return  Boolean true if group is a factor in a composite membership.
4877    */
4878   public boolean isComposite() {
4879     if ( GrouperDAOFactory.getFactory().getComposite().findAsFactor( this ).size() > 0 ) {
4880       return true;
4881     }
4882     return false;
4883   } // public boolean isComposite()
4884 
4885   /**
4886    * Revoke all privileges of the specified type on this group.
4887    * <pre class="eg">
4888    * try {
4889    *   g.revokePriv(AccessPrivilege.OPTIN);
4890    * }
4891    * catch (InsufficientPrivilegeException eIP) {
4892    *   // Not privileged to revoke this privilege
4893    * }
4894    * catch (RevokePrivilegeException eRP) {
4895    *   // Unable to modify group
4896    * }
4897    * </pre>
4898    * @param   priv  Revoke all instances of this privilege.
4899    * @throws  InsufficientPrivilegeException
4900    * @throws  RevokePrivilegeException
4901    * @throws  SchemaException
4902    */
4903   public void revokePriv(Privilege priv)
4904     throws  InsufficientPrivilegeException,
4905             RevokePrivilegeException,
4906             SchemaException
4907   {
4908     StopWatch sw = new StopWatch();
4909     sw.start();
4910     if ( !Privilege.isAccess(priv) ) {
4911       throw new SchemaException("attempt to use not access privilege: " + priv);
4912     }
4913     try {
4914       GrouperSession.staticGrouperSession().getAccessResolver().revokePrivilege(this, priv);
4915     }
4916     catch (UnableToPerformException eUTP) {
4917       throw new RevokePrivilegeException( eUTP.getMessage(), eUTP );
4918     }
4919     sw.stop();
4920     EVENT_LOG.groupRevokePriv(GrouperSession.staticGrouperSession(), this.getName(), priv, sw);
4921   } 
4922 
4923   /**
4924    * Revoke a privilege from the specified subject.
4925    * <pre class="eg">
4926    * try {
4927    *   g.revokePriv(subj, AccessPrivilege.OPTIN);
4928    * }
4929    * catch (InsufficientPrivilegeException e1) {
4930    *   // Not privileged to revoke this privilege
4931    * }
4932    * catch (RevokePrivilegeException eRP) {
4933    *   // Error revoking privilege
4934    * }
4935    * </pre>
4936    * @param   subj  Revoke privilege from this subject.
4937    * @param   priv  Revoke this privilege.
4938    * @throws  InsufficientPrivilegeException
4939    * @throws  RevokePrivilegeException
4940    * @throws  SchemaException
4941    */
4942   public void revokePriv(Subject subj, Privilege priv) 
4943     throws  InsufficientPrivilegeException,
4944             RevokePrivilegeException,
4945             SchemaException
4946   {
4947     revokePriv(subj, priv, true);
4948   }
4949   
4950   /**
4951    * Revoke a privilege from the specified subject.
4952    * <pre class="eg">
4953    * try {
4954    *   g.revokePriv(subj, AccessPrivilege.OPTIN);
4955    * }
4956    * catch (InsufficientPrivilegeException e1) {
4957    *   // Not privileged to revoke this privilege
4958    * }
4959    * catch (RevokePrivilegeException eRP) {
4960    *   // Error revoking privilege
4961    * }
4962    * </pre>
4963    * @param   subj  Revoke privilege from this subject.
4964    * @param   priv  Revoke this privilege.
4965    * @param exceptionIfAlreadyRevoked if false, and subject is already a member,
4966    * then dont throw a MemberAddException if the member is already in the group
4967    * @return false if it was already revoked, true if it wasnt already deleted
4968    * @throws  InsufficientPrivilegeException
4969    * @throws  RevokePrivilegeException
4970    * @throws  SchemaException
4971    */
4972   public boolean revokePriv(final Subject subj, final Privilege priv, 
4973       final boolean exceptionIfAlreadyRevoked) 
4974     throws  InsufficientPrivilegeException,
4975             RevokePrivilegeException, SchemaException {
4976 
4977     final StopWatch sw = new StopWatch();
4978     sw.start();
4979 
4980     final String errorMessageSuffix = ", group name: " + this.name 
4981     + ", subject: " + GrouperUtil.subjectToString(subj) + ", privilege: " + (priv == null ? null : priv.getName());
4982 
4983     return (Boolean)HibernateSession.callbackHibernateSession(
4984         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
4985           new HibernateHandler() {
4986 
4987       public Object callback(HibernateHandlerBean hibernateHandlerBean)
4988           throws GrouperDAOException {
4989     
4990         hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
4991 
4992         boolean wasntAlreadyRevoked = true;
4993         try {
4994           if ( !Privilege.isAccess(priv) ) {
4995             throw new SchemaException("attempt to use not access privilege: " + priv);
4996           }
4997           GrouperSession.staticGrouperSession().getAccessResolver().revokePrivilege(Group.this, subj, priv);
4998 
4999           if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
5000             
5001             Member member = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subj, false);
5002             
5003             AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.PRIVILEGE_GROUP_DELETE, "privilegeName", 
5004                 priv.getName(),  "memberId",  member.getUuid(),
5005                     "privilegeType", "access", 
5006                     "groupId", Group.this.getUuid(), "groupName", Group.this.getName());
5007                     
5008             auditEntry.setDescription("Deleted privilege: group: " + Group.this.getName()
5009                 + ", subject: " + subj.getSource().getId() + "." + subj.getId() + ", privilege: "
5010                 + priv.getName());
5011             auditEntry.saveOrUpdate(true);
5012           }
5013 
5014         } catch (UnableToPerformAlreadyExistsException eUTP) {
5015           if (exceptionIfAlreadyRevoked) {
5016             throw new RevokePrivilegeAlreadyRevokedException( eUTP.getMessage() + errorMessageSuffix, eUTP );
5017           }
5018           wasntAlreadyRevoked = false;
5019         } catch (UnableToPerformException eUTP) {
5020           throw new RevokePrivilegeException( eUTP.getMessage() + errorMessageSuffix, eUTP );
5021         }
5022         sw.stop();
5023         if (wasntAlreadyRevoked) {
5024           EVENT_LOG.groupRevokePriv(GrouperSession.staticGrouperSession(), Group.this.getName(), subj, priv, sw);
5025         }
5026         return wasntAlreadyRevoked;
5027       }
5028     });
5029   } 
5030 
5031   /**
5032    * Set an attribute value.
5033    * <pre class="eg">
5034    * try {
5035    *   g.attribute(attribute, value);
5036    * } 
5037    * catch (AttributeNotFoundException e0) {
5038    *   // Attribute doesn't exist
5039    * }
5040    * catch (GroupModifyException e1) {
5041    *   // Unable to modify group
5042    * }
5043    * catch (InsufficientPrivilegeException e2) {
5044    *   // Not privileged to modify this attribute
5045    * }
5046    * </pre>
5047    * @param   attributeName  Set this attribute.
5048    * @param   value Set to this value.
5049    * @throws  AttributeNotFoundException
5050    * @throws  GroupModifyException
5051    * @throws  InsufficientPrivilegeException
5052    * @deprecated
5053    */
5054   public void setAttribute(final String attributeName, final String value) 
5055     throws  AttributeNotFoundException, 
5056             GroupModifyException, 
5057             InsufficientPrivilegeException {
5058     setAttribute(attributeName, value, true);
5059   }
5060 
5061   /**
5062    * Set an attribute value.
5063    * <pre class="eg">
5064    * try {
5065    *   g.attribute(attribute, value);
5066    * } 
5067    * catch (AttributeNotFoundException e0) {
5068    *   // Attribute doesn't exist
5069    * }
5070    * catch (GroupModifyException e1) {
5071    *   // Unable to modify group
5072    * }
5073    * catch (InsufficientPrivilegeException e2) {
5074    *   // Not privileged to modify this attribute
5075    * }
5076    * </pre>
5077    * @param   attributeName  Set this attribute.
5078    * @param   value Set to this value.
5079    * @param   checkPrivileges 
5080    * @throws  AttributeNotFoundException
5081    * @throws  GroupModifyException
5082    * @throws  InsufficientPrivilegeException
5083    * @deprecated
5084    */
5085   public void setAttribute(final String attributeName, final String value, final boolean checkPrivileges) 
5086     throws  AttributeNotFoundException, 
5087             GroupModifyException, 
5088             InsufficientPrivilegeException {
5089     this.internal_setAttribute(attributeName, value, checkPrivileges, null);
5090   }
5091 
5092   /**
5093    * Set an attribute value.
5094    * <pre class="eg">
5095    * try {
5096    *   g.attribute(attribute, value);
5097    * } 
5098    * catch (AttributeNotFoundException e0) {
5099    *   // Attribute doesn't exist
5100    * }
5101    * catch (GroupModifyException e1) {
5102    *   // Unable to modify group
5103    * }
5104    * catch (InsufficientPrivilegeException e2) {
5105    *   // Not privileged to modify this attribute
5106    * }
5107    * </pre>
5108    * @param   attributeName  Set this attribute.
5109    * @param   value Set to this value.
5110    * @param   checkPrivileges 
5111    * @param uuid
5112    * @return the attribute
5113    * @throws  AttributeNotFoundException
5114    * @throws  GroupModifyException
5115    * @throws  InsufficientPrivilegeException
5116    */
5117   public Attribute internal_setAttribute(final String attributeName, final String value, final boolean checkPrivileges, final String uuid) 
5118     throws  AttributeNotFoundException, 
5119             GroupModifyException, 
5120             InsufficientPrivilegeException {
5121     
5122     return (Attribute)HibernateSession.callbackHibernateSession(
5123         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
5124         new HibernateHandler() {
5125 
5126           public Object callback(HibernateHandlerBean hibernateHandlerBean)
5127               throws GrouperDAOException {
5128             try {
5129 
5130               hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
5131 
5132               StopWatch sw = new StopWatch();
5133               sw.start();
5134         
5135               // TODO 20070531 split and test
5136               GrouperValidator v = NotNullOrEmptyValidator.validate(attributeName);
5137               if (v.isInvalid()) {
5138                 throw new AttributeNotFoundException(E.INVALID_ATTR_NAME + attributeName);
5139               }
5140               v = NotNullOrEmptyValidator.validate(value);
5141               if (v.isInvalid()) {
5142                 throw new GroupModifyException(E.INVALID_ATTR_VALUE + value);
5143               }
5144               
5145               String attributeDefPrefix = GrouperConfig.retrieveConfig().propertyValueStringRequired("legacyAttribute.attributeDef.prefix");
5146               AttributeDefName legacyAttribute = GrouperDAOFactory.getFactory().getAttributeDefName().findLegacyAttributeByName(attributeName, true);
5147               AttributeDef legacyAttributeDef = legacyAttribute.getAttributeDef();
5148               String groupTypeName = legacyAttributeDef.getExtension().substring(attributeDefPrefix.length());
5149               
5150               AttributeAssign groupTypeAssignment = Group.this.internal_getGroupTypeAssignments().get(groupTypeName);
5151               if (groupTypeAssignment == null) {
5152                 throw new AttributeNotFoundException("Group " + Group.this.getName() + " is not assigned the group type: " + groupTypeName);
5153               }
5154               
5155               if (checkPrivileges) {
5156                 groupTypeAssignment.getAttributeDelegate().assertCanUpdateAttributeDefName(legacyAttribute);
5157               }              
5158         //      if (_internal_fieldAttribute(attr)) {
5159         //        if (GrouperConfig.retrieveConfig().propertyValueBoolean("groups.allow.attribute.access.1.4", false)) {
5160         //          
5161         //          if (StringUtils.equals(FIELD_NAME, attr)) {
5162         //            this.setName(value);
5163         //          }
5164         //          if (StringUtils.equals(FIELD_EXTENSION, attr)) {
5165         //            this.setExtension(value);
5166         //          }
5167         //          if (StringUtils.equals(FIELD_DISPLAY_NAME, attr)) {
5168         //            this.setDisplayName(value);
5169         //          }
5170         //          if (StringUtils.equals(FIELD_DISPLAY_EXTENSION, attr)) {
5171         //            this.setDisplayExtension(value);
5172         //          }
5173         //          if (StringUtils.equals(FIELD_DESCRIPTION, attr)) {
5174         //            this.setDescription(value);
5175         //          }
5176         //          throw new RuntimeException("Not expecting attribute: " + attr);
5177         //          
5178         //        }
5179         //        throw new RuntimeException("Cannot access built in attribute: " + attr + " from setAttributes anymore, " +
5180         //            "use setter directly (e.g. setName(), setDisplayName()).  Or you can enable this (deprecated) with " +
5181         //            "grouper.properties setting groups.allow.attribute.access.1.4=true");
5182         //      }      
5183               
5184               //if this is not saved, then save it
5185               if (HibUtilsMapping.isInsert(Group.this)) {
5186                 Group.this.store();
5187               }
5188         
5189               Attribute attribute = Group.this.getAttributesMap(false).get(attributeName);
5190               if (attribute != null && StringUtils.equals(attribute.getValue(), value)) {
5191                 return null;
5192               }
5193               
5194               AuditTypeBuiltin auditTypeBuiltin = AuditTypeBuiltin.GROUP_ATTRIBUTE_UPDATE;
5195               String oldValue = null;
5196               String oldValueName = null;
5197               String verb = "Updated";
5198               if (attribute == null) {
5199                 auditTypeBuiltin = AuditTypeBuiltin.GROUP_ATTRIBUTE_ADD;
5200                 verb = "inserted";
5201               } else {
5202                 //if update, this is the old value
5203                 oldValue = attribute.getValue();
5204                 oldValueName = "oldValue";
5205               }
5206               
5207               AttributeAssign newOrExistingAssignment = groupTypeAssignment.getAttributeDelegate().internal_assignAttributeHelper(null, legacyAttribute, false, uuid, null).getAttributeAssign();
5208               AttributeAssignValueValue.html#AttributeAssignValue">AttributeAssignValue newAttributeAssignValue = new AttributeAssignValue();
5209               newAttributeAssignValue.setAttributeAssignId(newOrExistingAssignment.getId());
5210               newAttributeAssignValue.assignValue(value);
5211               AttributeAssignValueResult result = newOrExistingAssignment.getValueDelegate().internal_assignValue(newAttributeAssignValue, false);
5212                             
5213               Group.this.attributes.put(attributeName, result.getAttributeAssignValue());
5214               attribute = Group.this.getAttributesMap(false).get(attributeName);
5215 
5216               sw.stop();
5217               EVENT_LOG.groupSetAttr(GrouperSession.staticGrouperSession(), 
5218                   Group.this.getName(), attributeName, value, sw);
5219 
5220               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
5221                 
5222                 AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(auditTypeBuiltin, "id", 
5223                     attribute.getId(), "groupId", Group.this.getUuid(), 
5224                     "groupName", Group.this.getName(), "fieldId", null,
5225                     "fieldName", attributeName,  "value", attribute.getValue(), oldValueName, oldValue);
5226                 
5227                 auditEntry.setDescription(verb + " group attribute: " + attributeName + " on group: " 
5228                     + Group.this.getName() + " value: " + attribute.getValue() 
5229                     + (auditTypeBuiltin == AuditTypeBuiltin.GROUP_ATTRIBUTE_UPDATE ? (", oldValue: " + oldValue) : ""));
5230                 auditEntry.saveOrUpdate(true);
5231               }
5232               
5233               return attribute;
5234             }
5235             catch (GrouperDAOException eDAO) {
5236               throw new GroupModifyException( eDAO.getMessage(), eDAO );
5237             }
5238             catch (InsufficientPrivilegeException eIP) {
5239               throw eIP;
5240             }
5241           }
5242         });
5243   }
5244   
5245   /**
5246    * store this object to the DB.
5247    */
5248   public void store() {    
5249 
5250     GroupFinder.groupCacheRemove(this);
5251     
5252     validate();
5253     
5254     if (GrouperLoader.isDryRun()) {
5255       return;
5256     }
5257     
5258       HibernateSession.callbackHibernateSession(
5259           GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
5260           new HibernateHandler() {
5261 
5262             public Object callback(HibernateHandlerBean hibernateHandlerBean)
5263                 throws GrouperDAOException {
5264     
5265               hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
5266 
5267               Subject subject = GrouperSession.staticGrouperSession().getSubject();
5268               if (!Group.this.hasAdmin(subject)) {
5269                 throw new InsufficientPrivilegeException(GrouperUtil
5270                     .subjectToString(subject)
5271                     + " is not admin on group: " + Group.this.getName());
5272               }
5273                 
5274               if (Group.this.dbVersionDifferentFields().contains(FIELD_ALTERNATE_NAME_DB) &&
5275                   Group.this.getAlternateNameDb() != null) {
5276                   
5277                 // We're only checking create privilege on the stem if the alternate name
5278                 // is for another stem since prior to this, users could rename a group
5279                 // without having create privileges.
5280                 String parentStemName = GrouperUtil.parentStemNameFromName(Group.this.getAlternateNameDb());
5281                 if (parentStemName == null || !parentStemName.equals(Group.this.getParentStem().getName())) {
5282                   Stem stem = GrouperUtil.getFirstParentStemOfName(Group.this.getAlternateNameDb());
5283 
5284                   if (!stem.hasCreate(subject) && !stem.hasStemAdmin(subject)) {
5285                     throw new InsufficientPrivilegeException(GrouperUtil.subjectToString(subject)
5286                         + " cannot create in stem: " + stem.getName());
5287                   }
5288                 }
5289               }
5290               
5291               // GRP-1091: ldap loader display name doesnt change
5292               if (GrouperConfig.retrieveConfig().propertyValueBoolean("grouper.rename.includeExcludeRequireEtc.when.name.changes", true)) {
5293                 Group.this.changeDisplayProperties();
5294               }
5295               
5296               if (Group.this.dbVersionDifferentFields().contains(FIELD_TYPE_OF_GROUP)) {
5297                 TypeOfGroup oldTypeOfGroup = Group.this.dbVersion().getTypeOfGroup();
5298                 TypeOfGroup newTypeOfGroup = Group.this.getTypeOfGroup();
5299                 
5300                 if (oldTypeOfGroup.equals(TypeOfGroup.group) && newTypeOfGroup.equals(TypeOfGroup.role)) {
5301                   // converting group to role
5302                   RoleSet existingRoleSet = GrouperDAOFactory.getFactory().getRoleSet().findSelfRoleSet(Group.this.getUuid(), false);
5303                   if (existingRoleSet == null) {
5304                     RoleSeter/permissions/role/RoleSet.html#RoleSet">RoleSet roleSet = new RoleSet();
5305                     roleSet.setId(GrouperUuid.getUuid());
5306                     roleSet.setDepth(0);
5307                     roleSet.setIfHasRoleId(Group.this.getId());
5308                     roleSet.setThenHasRoleId(Group.this.getId());
5309                     roleSet.setType(RoleHierarchyType.self);
5310                     roleSet.setParentRoleSetId(roleSet.getId());
5311                     roleSet.saveOrUpdate();
5312                   }
5313                 } else if (oldTypeOfGroup.equals(TypeOfGroup.role) && newTypeOfGroup.equals(TypeOfGroup.group)) {
5314                   // converting role to group
5315                   Set<RoleSet> roleSets = GrouperDAOFactory.getFactory().getRoleSet().findByIfHasRoleIdImmediate(Group.this.getUuid());
5316                   Iterator<RoleSet> iter = roleSets.iterator();
5317                   while (iter.hasNext()) {
5318                     RoleSet roleSet = iter.next();
5319                     roleSet.getIfHasRole().getRoleInheritanceDelegate().removeRoleFromInheritFromThis(roleSet.getThenHasRole());
5320                   }
5321                   
5322                   roleSets = GrouperDAOFactory.getFactory().getRoleSet().findByThenHasRoleIdImmediate(Group.this.getUuid());
5323                   iter = roleSets.iterator();
5324                   while (iter.hasNext()) {
5325                     RoleSet roleSet = iter.next();
5326                     roleSet.getIfHasRole().getRoleInheritanceDelegate().removeRoleFromInheritFromThis(roleSet.getThenHasRole());
5327                   }
5328                   
5329                   RoleSet selfRoleSet = GrouperDAOFactory.getFactory().getRoleSet().findSelfRoleSet(Group.this.getUuid(), false);
5330                   if (selfRoleSet != null) {
5331                     selfRoleSet.delete();
5332                   }
5333 
5334                   //delete any attributes on this stem, this is done as root
5335                   GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
5336                     
5337                     public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
5338                       Set<AttributeAssign> attributeAssigns = GrouperDAOFactory.getFactory().getAttributeAssign().findByOwnerGroupId(Group.this.getUuid());
5339                       Iterator<AttributeAssign> attributeAssignsIter = attributeAssigns.iterator();
5340                       while (attributeAssignsIter.hasNext()) {
5341                         AttributeAssign attributeAssign = attributeAssignsIter.next();
5342                         if (attributeAssign.getAttributeDef().getAttributeDefType().equals(AttributeDefType.perm)) {
5343                           attributeAssign.delete();
5344                         }
5345                       }
5346                       return null;
5347                     }
5348                   });
5349 
5350                 }
5351 
5352               }
5353 
5354               
5355               String differences = GrouperUtil.dbVersionDescribeDifferences(Group.this.dbVersion(), 
5356                   Group.this, Group.this.dbVersion() != null ? Group.this.dbVersionDifferentFields() : Group.CLONE_FIELDS);
5357               
5358               boolean enabling = Group.this.dbVersion() != null && Group.this.isEnabled() && !Group.this.dbVersion().isEnabled();
5359               boolean disabling = Group.this.dbVersion() != null && !Group.this.isEnabled() && Group.this.dbVersion().isEnabled();
5360 
5361               GrouperDAOFactory.getFactory().getGroup().update( Group.this );
5362               
5363               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
5364                 AuditEntry auditEntry = null;
5365                 
5366                 if (enabling) {
5367                   auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_ENABLE, "id", 
5368                       Group.this.getUuid(), "name", Group.this.getName(), "parentStemId", Group.this.getParentUuid(), 
5369                       "displayName", Group.this.getDisplayName(), "description", Group.this.getDescription());
5370                   auditEntry.setDescription("Enabled group: " + Group.this.getName() + ", " + differences);
5371                 } else if (disabling) {
5372                   auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_DISABLE, "id", 
5373                       Group.this.getUuid(), "name", Group.this.getName(), "parentStemId", Group.this.getParentUuid(), 
5374                       "displayName", Group.this.getDisplayName(), "description", Group.this.getDescription());
5375                   auditEntry.setDescription("Disabled group: " + Group.this.getName() + ", " + differences);
5376                 } else if (Group.this.typeOfGroup == TypeOfGroup.entity) {
5377                   
5378                   auditEntry = new AuditEntry(AuditTypeBuiltin.ENTITY_UPDATE, "id", 
5379                       Group.this.getUuid(), "name", Group.this.getName(), "parentStemId", Group.this.getParentUuid(), 
5380                       "displayName", Group.this.getDisplayName(), "description", Group.this.getDescription());
5381                   auditEntry.setDescription("Updated entity: " + Group.this.getName() + ", " + differences);
5382 
5383                 } else {
5384                   auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_UPDATE, "id", 
5385                       Group.this.getUuid(), "name", Group.this.getName(), "parentStemId", Group.this.getParentUuid(), 
5386                       "displayName", Group.this.getDisplayName(), "description", Group.this.getDescription());
5387                   auditEntry.setDescription("Updated group: " + Group.this.getName() + ", " + differences);
5388 
5389                 }
5390                 
5391                 auditEntry.saveOrUpdate(true);
5392               }
5393               
5394               return null;
5395             }
5396           });
5397       
5398   }
5399 
5400   /**
5401    * 
5402    */
5403   public void validate() {
5404     //lets validate
5405     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(groupsTable, "name", 
5406     //        Types.VARCHAR, ddlVersionBean.isSqlServer() ? "900" : "1024", false, false);
5407     boolean sqlServer = GrouperDdlUtils.isSQLServer();
5408     int maxNameLength = sqlServer ? 900 : 1024;
5409     maxNameLength = GrouperConfig.retrieveConfig().propertyValueInt("grouper.groupName.maxSize", maxNameLength);
5410     
5411     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(groupsTable, "extension", 
5412     //        Types.VARCHAR, "255", false, false);
5413     if (GrouperUtil.lengthAscii(this.getExtension()) > 255 ) {
5414       throw new GrouperValidationException("Group extension too long: " + GrouperUtil.lengthAscii(this.getExtension()), 
5415           VALIDATION_GROUP_EXTENSION_TOO_LONG_KEY, 255, GrouperUtil.lengthAscii(this.getExtension()));
5416     }
5417 
5418     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(groupsTable, "display_extension", 
5419     //        Types.VARCHAR, "255", false, false);
5420     if (GrouperUtil.lengthAscii(this.getDisplayExtension()) > 255 ) {
5421       throw new GrouperValidationException("Group display extension too long: " + GrouperUtil.lengthAscii(this.getDisplayExtension()), 
5422           VALIDATION_GROUP_DISPLAY_EXTENSION_TOO_LONG_KEY, 255, GrouperUtil.lengthAscii(this.getDisplayExtension()));
5423     }
5424 
5425     
5426     if (GrouperUtil.lengthAscii(this.getName()) > maxNameLength) {
5427       throw new GrouperValidationException("Group name too long: " + GrouperUtil.lengthAscii(this.getName()), 
5428           VALIDATION_GROUP_NAME_TOO_LONG_KEY, maxNameLength, GrouperUtil.lengthAscii(this.getName()));
5429     }
5430 
5431     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(groupsTable, "display_name", 
5432     //        Types.VARCHAR, ddlVersionBean.isSqlServer() ? "900" : "1024", false, false);
5433     if (GrouperUtil.lengthAscii(this.getDisplayName()) > maxNameLength) {
5434       throw new GrouperValidationException("Group display name too long: " + GrouperUtil.lengthAscii(this.getDisplayName()), 
5435           VALIDATION_GROUP_DISPLAY_NAME_TOO_LONG_KEY, maxNameLength, GrouperUtil.lengthAscii(this.getDisplayName()));
5436     }
5437 
5438     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(groupsTable, "description", 
5439     //        Types.VARCHAR, "1024", false, false);
5440     if (GrouperUtil.lengthAscii(this.getDescription()) > 1024 ) {
5441       throw new GrouperValidationException("Group description too long: " + GrouperUtil.lengthAscii(this.getDescription()), 
5442           VALIDATION_GROUP_DESCRIPTION_TOO_LONG_KEY, 1024, GrouperUtil.lengthAscii(this.getDescription()));
5443     }
5444   }
5445 
5446   /**
5447    * Set group description.  
5448    * Note, you have to call store() at some point to 
5449    * make the kick off the sql
5450    * <pre class="eg">
5451    * try {
5452    *   g.setDescription(value);
5453    * }
5454    * catch (GroupModifyException e0) {
5455    *   // Unable to modify group
5456    * }
5457    * catch (InsufficientPrivilegeException e1) {
5458    *   // Not privileged to modify description
5459    * }
5460    * </pre>
5461    * @param   value   Set description to this value.
5462    */
5463   public void setDescription(String value) {
5464     this.description = value;
5465   }
5466 
5467   /**
5468    * Set group description (hibernate method).  
5469    * @param   value   Set description to this value.
5470    */
5471   public void setDescriptionDb(String value) {
5472     this.description = value;
5473   }
5474   
5475   /**
5476    * Set group <i>extension</i>.
5477    * Note, you have to call store() at some point to 
5478    * make the kick off the sql
5479    * <pre class="eg">
5480    *   g.setExtension(value);
5481    * </pre>
5482    * @param   value   Set <i>extension</i> to this value.
5483    */
5484   public void setExtension(String value) {
5485     setExtension(value, true);
5486   }
5487  
5488   /**
5489    * Set group <i>extension</i>.
5490    * Note, you have to call store() at some point to 
5491    * make the kick off the sql
5492    * <pre class="eg">
5493    *   g.setExtension(value, true);
5494    * </pre>
5495    * @param   value   Set <i>extension</i> to this value.
5496    * @param assignAlternateName Whether to add the old group name as an 
5497    *                            alternate name for the renamed group.
5498    */
5499   public void setExtension(String value, boolean assignAlternateName) {
5500     NamingValidator v = NamingValidator.validate(value);
5501     if (v.isInvalid()) {
5502       throw new GroupModifyException( v.getErrorMessage() );
5503     }
5504 
5505     String oldExtension = null;
5506     if (this.dbVersion() != null) {
5507       oldExtension = this.dbVersion().getExtensionDb();
5508     }
5509     
5510     if (assignAlternateName && oldExtension != null && !oldExtension.equals(value)) {
5511       internal_addAlternateName(this.dbVersion().getNameDb(), false);
5512     }
5513     this.extension = value;
5514     this.setNameDb(U.constructName( this.getParentStem().getName(), value ) );
5515 
5516   }
5517 
5518   /**
5519    * Set group <i>name</i>.  This should not be called
5520    * @param   value   Set <i>extension</i> to this value.
5521    */
5522   public void setName(String value) {
5523     throw new InsufficientPrivilegeException("group name is system maintained: " + this.name + ", " + value);
5524   }
5525 
5526   /**
5527    * Set group displayExtension.
5528    * Note, you have to call store() at some point to 
5529    * make the kick off the sql
5530    * <pre class="eg">
5531    *   g.setDisplayExtension(value);
5532    * </pre>
5533    * @param   value   Set displayExtension to this value.
5534    */
5535   public void setDisplayExtension(String value) {
5536     NamingValidator v = NamingValidator.validate(value);
5537     if (v.isInvalid()) {
5538       throw new GroupModifyException( v.getErrorMessage() );
5539     }
5540 
5541     this.displayExtension = value;
5542     this.setDisplayNameDb(U.constructName( this.getParentStem().getDisplayName(), value ) );
5543 
5544   }
5545 
5546   /**
5547    * hibernate method
5548    * @param value
5549    */
5550   public void setDisplayExtensionDb(String value) {
5551     this.displayExtension = value;
5552   }
5553   
5554   /**
5555    * hibernate method
5556    * @param value
5557    */
5558   public void setExtensionDb(String value) {
5559     this.extension = value;
5560   }
5561   
5562   /**
5563    * hibernate method
5564    * @return display extension
5565    */
5566   public String getDisplayExtensionDb() {
5567     return this.displayExtension;
5568   }
5569   
5570   /**
5571    * hibernate method
5572    * @return extension
5573    */
5574   public String getExtensionDb() {
5575     return this.extension;
5576   }
5577   
5578   /**
5579    * This is really only for hibernate
5580    * @param value new display name
5581    */
5582   public void setDisplayName(String value) {
5583     throw new InsufficientPrivilegeException("group display name is system maintained: " + this.name + ", " + value);
5584   }
5585 
5586   /**
5587    * Convert this group to a {@link Member} object.
5588    * <p/>
5589    * <pre class="eg">
5590    * Member m = g.toMember();
5591    * </pre>
5592    * @return  {@link Group} as a {@link Member}
5593    * @throws  GrouperException
5594    */
5595   public Member toMember() 
5596     throws  GrouperException
5597   {
5598     if ( this.cachedMember != null ) {
5599       return this.cachedMember;
5600     }
5601     try {
5602       GrouperSession.validate( GrouperSession.staticGrouperSession() );
5603       Member m = GrouperDAOFactory.getFactory().getMember().findBySubject( this.toSubject(), true );
5604       GrouperSession.staticGrouperSession();
5605       this.cachedMember = m;
5606       return this.cachedMember;
5607     }  
5608     catch (MemberNotFoundException eMNF) {
5609       // If we can't convert a group to a member we have major issues
5610       // and should probably just give up
5611       String msg = E.GROUP_G2M + eMNF.getMessage();
5612       LOG.fatal(msg);
5613       throw new GrouperException(msg, eMNF);
5614     }
5615   } // public Member toMember()
5616 
5617   /**
5618    * Convert this group to a {@link Subject} object.
5619    * <p/>
5620    * <pre class="eg">
5621    * Subject subj = g.toSubject();
5622    * </pre>
5623    * @return  {@link Group} as a {@link Subject}
5624    * @throws  GrouperException
5625    */
5626   public Subject toSubject() 
5627     throws  GrouperException
5628   {
5629     if ( this.subjectCache.containsKey(KEY_SUBJECT) ) {
5630       return this.subjectCache.get(KEY_SUBJECT);
5631     }
5632     try {
5633       
5634       GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
5635         
5636         public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
5637     
5638          if (Group.this.getTypeOfGroup() == TypeOfGroup.entity) {
5639            Group.this.subjectCache.put(
5640                KEY_SUBJECT, SubjectFinder.findByIdAndSource( Group.this.getUuid(), SubjectFinder.internal_getEntitySourceAdapter(true).getId(), true )
5641              );
5642         
5643           } else {
5644            Group.this.subjectCache.put(
5645              KEY_SUBJECT, SubjectFinder.findByIdAndSource( Group.this.getUuid(), SubjectFinder.internal_getGSA().getId(), true )
5646            );
5647           }
5648         return null;
5649         }
5650       });
5651         
5652       return this.subjectCache.get(KEY_SUBJECT);
5653     }
5654     catch (SourceUnavailableException eShouldNeverHappen0)  {
5655       String msg = E.GROUP_G2S + eShouldNeverHappen0.getMessage();
5656       LOG.fatal(msg, eShouldNeverHappen0);
5657       throw new GrouperException(msg, eShouldNeverHappen0);
5658     }
5659     catch (SubjectNotFoundException eShouldNeverHappen1)    {
5660       String msg = E.GROUP_G2S + eShouldNeverHappen1.getMessage();
5661       LOG.fatal(msg, eShouldNeverHappen1);
5662       throw new GrouperException(msg, eShouldNeverHappen1);
5663     }
5664     catch (SubjectNotUniqueException eShouldNeverHappen2)   {
5665       String msg = E.GROUP_G2S + eShouldNeverHappen2.getMessage();
5666       LOG.fatal(msg);
5667       throw new GrouperException(msg, eShouldNeverHappen2);
5668     }
5669   } // public Subject toSubject()
5670 
5671   /**
5672    * @see java.lang.Object#toString()
5673    */
5674   public String toString() {
5675     // Bypass privilege checks.  If the group is loaded it is viewable.
5676     return new ToStringBuilder(this)
5677       .append( "name", this.name)
5678       .append( "uuid", this.getUuid() )
5679       .toString();
5680   }
5681 
5682 
5683   /**
5684    * TODO 20070531 make into some flavor of validator
5685    * @param subj 
5686    * @param f 
5687    * @return  boolean
5688    * @throws IllegalArgumentException 
5689    * @throws SchemaException 
5690    * @since   1.2.1
5691    */
5692   public boolean internal_canWriteField(Subject subj, Field f)
5693     throws  IllegalArgumentException,
5694             SchemaException
5695   {
5696     GrouperValidator v = NotNullValidator.validate(subj);
5697     if (v.isInvalid()) {
5698       throw new IllegalArgumentException( "subject: " + v.getErrorMessage() );
5699     } 
5700     v = NotNullValidator.validate(f);
5701     if (v.isInvalid()) {
5702       throw new IllegalArgumentException( "field: " + v.getErrorMessage() );
5703     }
5704     v = FieldTypeValidator.validate(f);
5705     if (v.isInvalid()) {
5706       throw new SchemaException( v.getErrorMessage() );
5707     }
5708     GroupType groupType = f.getGroupType(false);
5709     if (groupType != null && !this.hasType(groupType)) {
5710       throw new SchemaException( E.INVALID_GROUP_TYPE + " for group name: " + this.getName() + ", " + groupType.toString() + ":" + f.getName() );
5711     }
5712     try {
5713       PrivilegeHelper.dispatch( GrouperSession.staticGrouperSession(), this, subj, f.getWritePriv() );
5714       return true;
5715     }
5716     catch (InsufficientPrivilegeException eIP) {
5717       return false;
5718     }
5719   }
5720 
5721   /** if true, then dont set the modified stuff on save */
5722   @GrouperIgnoreClone @GrouperIgnoreFieldConstant @GrouperIgnoreDbVersion
5723   private boolean dontSetModified = false;
5724 
5725   /**
5726    * if we should not set the modified attributes on group save
5727    * @param theDontSetModified
5728    */
5729   public void setDontSetModified(boolean theDontSetModified) {
5730     this.dontSetModified = theDontSetModified;
5731   }
5732   
5733   /**
5734    * 
5735    */
5736   private void internal_setModifiedIfNeeded() {
5737     if (!dontSetModified) {
5738       this.setModifierUuid( GrouperSession.staticGrouperSession().getMember().getUuid() );
5739       this.setModifyTimeLong( System.currentTimeMillis() );
5740     }
5741   }
5742 
5743   /**
5744    * 
5745    * @param session
5746    * @param type
5747    * @param left
5748    * @param right
5749    * @param uuid 
5750    * @return composite
5751    * @throws InsufficientPrivilegeException
5752    * @throws MemberAddException
5753    */
5754   public Composite internal_addCompositeMember(final GrouperSession session, final CompositeType type,
5755       final Grouprouper/Group.html#Group">Group left, final Group right, final String uuid) throws InsufficientPrivilegeException, MemberAddException {
5756 
5757     //dont allow anything to be an entity... can only be a group or role
5758     if (this.getTypeOfGroup() == TypeOfGroup.entity || left.getTypeOfGroup() == TypeOfGroup.entity 
5759         || right.getTypeOfGroup() == TypeOfGroup.entity) {
5760       throw new RuntimeException("Cannot add composite to an entity");
5761     }
5762     
5763     final String errorMessageSuffix = ", group name: " + this.name + ", compositeType: " + type
5764       + ", left group name: " + (left == null ? "null" : left.getName()) 
5765       + ", right group name: " + (right == null ? "null" : right.getName());
5766 
5767   return (Composite)HibernateSession.callbackHibernateSession(
5768       GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
5769       new HibernateHandler() {
5770 
5771         public Object callback(HibernateHandlerBean hibernateHandlerBean)
5772         throws GrouperDAOException {
5773           try {
5774 
5775             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
5776 
5777             StopWatch sw = new StopWatch();
5778             sw.start();
5779       
5780             PrivilegeHelper.dispatch(session, Group.this, session.getSubject(), Group
5781                 .getDefaultList().getWritePriv());
5782             PrivilegeHelper.dispatch(session, left, session.getSubject(), Group
5783                 .getDefaultList().getReadPriv());
5784             PrivilegeHelper.dispatch(session, right, session.getSubject(), Group
5785                 .getDefaultList().getReadPriv());
5786 
5787             Compositerouper/Composite.html#Composite">Composite c = new Composite();
5788             c.setCreateTime(new Date().getTime());
5789             c.setCreatorUuid(session.getMember().getUuid());
5790             c.setFactorOwnerUuid(Group.this.getUuid());
5791             c.setLeftFactorUuid(left.getUuid());
5792             c.setRightFactorUuid(right.getUuid());
5793             c.setTypeDb(type.toString());
5794             c.setUuid(StringUtils.isBlank(uuid) ? GrouperUuid.getUuid() : uuid);
5795             CompositeValidator vComp = CompositeValidator.validate(c);
5796             if (vComp.isInvalid()) {
5797               throw new MemberAddException(vComp.getErrorMessage() + ", " + errorMessageSuffix);
5798             }
5799       
5800             AddCompositeMemberValidator vAdd = AddCompositeMemberValidator.validate(Group.this);
5801             if (vAdd.isInvalid()) {
5802               throw new MemberAddException(vAdd.getErrorMessage() + ", " + errorMessageSuffix);
5803             }
5804 
5805             GrouperDAOFactory.getFactory().getComposite().save(c);
5806             EVENT_LOG.groupAddComposite(session, c, sw);
5807             
5808             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
5809               
5810               AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_COMPOSITE_ADD, "id", 
5811                   c.getUuid(), "ownerId", Group.this.getUuid(), "ownerName", Group.this.getName(), "leftFactorId", 
5812                   left.getUuid(), "leftFactorName", left.getName(), "rightFactorId", right.getUuid(), 
5813                   "rightFactorName", right.getName(), "type", type.toString());
5814               auditEntry.setDescription("Added composite: " + Group.this.getName() + " is " 
5815                   + left.getName() + " " + type.toString() + " " + right.getName());
5816               auditEntry.saveOrUpdate(true);
5817             }
5818 
5819             // since the composite add could have taken a while if there were many memberships to add,
5820             // make sure there weren't any immediate memberships added in the meantime..
5821             // note that since the commit isn't done yet, this issue can still happen,
5822             // but it would hopefully be much less likely..
5823             Set<Membership> immediateMemberships = GrouperDAOFactory.getFactory().getMembership().findAllMembershipEntriesByGroupOwnerAndFieldAndType(Group.this.getUuid(), Group.getDefaultList(), "immediate", false);
5824             if (immediateMemberships.size() > 0) {
5825               throw new IllegalStateException("Immediate memberships were added to this group (name=" + Group.this.getName() + ") while making it a composite group");
5826             }
5827             
5828             sw.stop();
5829             return c;
5830           } catch (SchemaException eS) {
5831             GrouperUtil.injectInException(eS, errorMessageSuffix);
5832             throw new MemberAddException(eS);
5833           } catch (RuntimeException re) {
5834             GrouperUtil.injectInException(re, errorMessageSuffix);
5835             throw re;
5836           }
5837         }
5838       });
5839   }
5840 
5841   /**
5842    * 
5843    * @throws InsufficientPrivilegeException
5844    * @throws RevokePrivilegeException
5845    * @throws SchemaException
5846    */
5847   private void _revokeAllAccessPrivs() 
5848     throws  InsufficientPrivilegeException,
5849             RevokePrivilegeException, 
5850             SchemaException {
5851 
5852     try {
5853       GrouperSession.callbackGrouperSession(GrouperSession.staticGrouperSession().internal_getRootSession(), new GrouperSessionHandler() {
5854   
5855         public Object callback(GrouperSession grouperSession)
5856             throws GrouperSessionException {
5857           try {
5858             Group.this.revokePriv(AccessPrivilege.ADMIN);
5859             Group.this.revokePriv(AccessPrivilege.OPTIN);
5860             Group.this.revokePriv(AccessPrivilege.OPTOUT);
5861             Group.this.revokePriv(AccessPrivilege.READ);
5862             Group.this.revokePriv(AccessPrivilege.UPDATE);
5863             Group.this.revokePriv(AccessPrivilege.VIEW);
5864             Group.this.revokePriv(AccessPrivilege.GROUP_ATTR_READ);
5865             Group.this.revokePriv(AccessPrivilege.GROUP_ATTR_UPDATE);
5866           } catch (InsufficientPrivilegeException ipe) {
5867             throw new GrouperSessionException(ipe);
5868           } catch (SchemaException ipe) {
5869             throw new GrouperSessionException(ipe);
5870           } catch (RevokePrivilegeException ipe) {
5871             throw new GrouperSessionException(ipe);
5872           }
5873           return null;
5874         }
5875         
5876       });
5877     } catch (GrouperSessionException gse) {
5878       if (gse.getCause() instanceof InsufficientPrivilegeException) {
5879         throw (InsufficientPrivilegeException)gse.getCause();
5880       }
5881       if (gse.getCause() instanceof SchemaException) {
5882         throw (SchemaException)gse.getCause();
5883       }
5884       if (gse.getCause() instanceof RevokePrivilegeException) {
5885         throw (RevokePrivilegeException)gse.getCause();
5886       }
5887       throw gse;
5888     }
5889 
5890 
5891   } // private void _revokeAllAccessPrivs()
5892 
5893   
5894   /**
5895    * @return the set of different fields
5896    * @see edu.internet2.middleware.grouper.GrouperAPI#dbVersionDifferentFields()
5897    */
5898   public Set<String> dbVersionDifferentFields() {
5899     return dbVersionDifferentFields(true);
5900     
5901   }
5902   
5903 
5904   
5905   /**
5906    * @param failIfNull 
5907    * @return the set of different fields
5908    * @see edu.internet2.middleware.grouper.GrouperAPI#dbVersionDifferentFields()
5909    */
5910   public Set<String> dbVersionDifferentFields(boolean failIfNull) {
5911     if (this.dbVersion == null) {
5912       if (failIfNull) {
5913         throw new RuntimeException("State was never stored from db");
5914       }
5915       return null;
5916     }
5917     //easier to unit test if everything is ordered
5918     Set<String> result = GrouperUtil.compareObjectFields(this, this.dbVersion,
5919         DB_VERSION_FIELDS, null);
5920 
5921     return result;
5922   }
5923 
5924   /**
5925    * take a snapshot of the data since this is what is in the db
5926    */
5927   @Override
5928   public void dbVersionReset() {
5929     //lets get the state from the db so we know what has changed
5930     this.dbVersion = GrouperUtil.clone(this, DB_VERSION_FIELDS);
5931   }
5932 
5933   /**
5934    * deep clone the fields in this object
5935    */
5936   @Override
5937   public Group clone() {
5938     return GrouperUtil.clone(this, CLONE_FIELDS);
5939   }
5940 
5941   /**
5942    * 
5943    * @see java.lang.Object#equals(java.lang.Object)
5944    */
5945   public boolean equals(Object other) {
5946     if (this == other) {
5947       return true;
5948     }
5949     if (!(other instanceof Group)) {
5950       return false;
5951     }
5952     return new EqualsBuilder()
5953       .append( this.getName(), ( (Group) other ).getName() )
5954       .isEquals();
5955   } // public boolean equals(other)
5956 
5957   /**
5958    * init attributes etc for multiple groups at once
5959    * @param groups
5960    */
5961   public static void initData(Collection<Group> groups) {
5962 
5963     Collection<String> groupIds = new HashSet<String>();
5964     
5965     for (Group group : GrouperUtil.nonNull(groups)) {
5966       if (group.attributes == null) {
5967         groupIds.add(group.getId());
5968       }
5969     }
5970     
5971     if (GrouperUtil.length(groupIds) == 0) {
5972       return;
5973     }
5974     
5975     Map<String, Map<String, AttributeAssignValue>> results = GrouperDAOFactory
5976         .getFactory().getAttributeAssignValue().findLegacyAttributesByGroupIds(groupIds);
5977 
5978     for (Group group : GrouperUtil.nonNull(groups)) {
5979       if (group.attributes == null) {
5980         Map<String, AttributeAssignValue> theAttributes = results.get(group.getId());
5981         group.attributes = theAttributes;
5982       }
5983     }
5984     
5985   }
5986   
5987   /**
5988    * This is a direct access to the attributes (going around security checking etc).
5989    * You probably shouldnt be calling this if not on the grouper team.
5990    * @param checkSecurity if we should check if the current user can see each attribute
5991    * 
5992    * @return attributes, will never be null
5993    * @since   1.2.0
5994    * @deprecated
5995    */
5996   public Map<String, Attribute> getAttributesMap(boolean checkSecurity) {
5997   
5998     if (this.attributes == null) {
5999       this.attributes = GrouperDAOFactory.getFactory().getAttributeAssignValue().findLegacyAttributesByGroupId(this.getUuid());
6000     }
6001     
6002     Map<String, Attribute> results = new HashMap<String, Attribute>();
6003     for (String name : this.attributes.keySet()) {
6004       AttributeAssignValue value = this.attributes.get(name);
6005       
6006       try {
6007         if (checkSecurity) {
6008           value.getAttributeAssign().getOwnerAttributeAssign().getAttributeDelegate().assertCanReadAttributeDef(value.getAttributeAssign().getAttributeDef());
6009         }
6010         
6011         results.put(name, Attribute.internal_getAttribute(value, this, true));
6012       } catch (InsufficientPrivilegeException e) {
6013         // okay, simply not allowed to see attribute
6014       } catch (AttributeDefNotFoundException e) {
6015         // okay, simply not allowed to see attribute
6016       }
6017     }
6018     
6019     return results;
6020   }
6021 
6022   /**
6023    * This is a direct access to the attributes (going around security checking etc).
6024    * Use the other get attributes map method
6025    * 
6026    * @return attributes
6027    * @since   1.2.0
6028    */
6029   @Deprecated
6030   public Map<String, String> getAttributesDb() {
6031 
6032 //      if (GrouperConfig.retrieveConfig().propertyValueBoolean("groups.allow.attribute.access.1.4", false)) {
6033 //        
6034 //        String theName = this.getName();
6035 //        if (!StringUtils.isBlank(theName)) {
6036 //          this.attributes.put("name", theName);
6037 //        }
6038 //        String theDisplayName = this.getDisplayName();
6039 //        if (!StringUtils.isBlank(theDisplayName)) {
6040 //          this.attributes.put("displayName", theDisplayName);
6041 //        }
6042 //        String theExtension = this.getExtension();
6043 //        if (!StringUtils.isBlank(theExtension)) {
6044 //          this.attributes.put("extension", theExtension);
6045 //        }
6046 //        String theDisplayExtension = this.getDisplayExtension();
6047 //        if (!StringUtils.isBlank(theDisplayExtension)) {
6048 //          this.attributes.put("displayExtension", theDisplayExtension);
6049 //        }
6050 //        String theDescription = this.getDescription();
6051 //        if (!StringUtils.isBlank(theDescription)) {
6052 //          this.attributes.put("description", theDescription);
6053 //        }
6054 //      }
6055 
6056     //init
6057     Map<String, Attribute> results = this.getAttributesMap(false);
6058     
6059     Map<String, String> map = new HashMap<String, String>();
6060     for (String key : results.keySet()) {
6061       map.put(key, results.get(key).getValue());
6062     }
6063     return map;
6064   }
6065 
6066   /**
6067    * @return create time
6068    * @since   1.2.0
6069    */
6070   public long getCreateTimeLong() {
6071     return this.createTime;
6072   }
6073 
6074   /**
6075    * @return creator
6076    * @since   1.2.0
6077    */
6078   public String getCreatorUuid() {
6079     return this.creatorUUID;
6080   }
6081 
6082   /**
6083    * 
6084    * @see edu.internet2.middleware.grouper.GrouperAPI#dbVersion()
6085    */
6086   @Override
6087   public Group dbVersion() {
6088     return (Group)this.dbVersion;
6089   }
6090 
6091   /**
6092    * 
6093    * @return uuid
6094    */
6095   public String getModifierUuid() {
6096     return this.modifierUUID;
6097   }
6098 
6099   /**
6100    * @return  modify time
6101    * @since   1.2.0
6102    */
6103   public long getModifyTimeLong() {
6104     return this.modifyTime;
6105   }
6106 
6107   /**
6108    * @return parent uuid
6109    * @since   1.2.0
6110    */
6111   public String getParentUuid() {
6112     return this.parentUuid;
6113   }
6114 
6115   /**
6116    * @return uuid
6117    * @since   1.2.0
6118    */
6119   public String getUuid() {
6120     return this.uuid;
6121   }
6122 
6123   /**
6124    * @return hashcode
6125    * @since   1.2.0
6126    */
6127   public int hashCode() {
6128     return new HashCodeBuilder()
6129       .append( this.getName() )
6130       .toHashCode();
6131   } // public int hashCode()
6132 
6133   /**
6134    * 
6135    * @see edu.internet2.middleware.grouper.GrouperAPI#onDelete(org.hibernate.Session)
6136    */
6137   @Override
6138   public boolean onDelete(Session hs) 
6139     throws  CallbackException {
6140     GrouperDAOFactory.getFactory().getGroup().putInExistsCache( this.getUuid(), false );
6141     GroupFinder.groupCacheRemove(this);
6142     return Lifecycle.NO_VETO;
6143   }
6144 
6145 //  /** instance var of attributes to compare, so if a hook does this we are all set */
6146 //  private Map _internateAttributesToCompare = null;
6147 //
6148 //  /**
6149 //   * update the attributes for a group
6150 //   * @param hibernateSession 
6151 //   * @param checkExisting true if an update, false if insert
6152 //   */
6153 //  public void _updateAttributes(HibernateSession hibernateSession, boolean checkExisting) {
6154 //    ByObject byObject = hibernateSession.byObject();
6155 //
6156 //    Map                   attrs = new HashMap(this.getAttributesDb());
6157 //    String                k;
6158 //asdf
6159 //    List<Attribute> attributes = checkExisting ? GrouperDAOFactory.getFactory().getGroup()._getAttributes(hibernateSession, this) : null;
6160 //    for (Attribute attribute : GrouperUtil.nonNull(attributes)) {
6161 //      k = attribute.getAttrName();
6162 //      if ( attrs.containsKey(k) ) {
6163 //        // attr both in db and in memory.  compare.
6164 //        if ( !attribute.getValue().equals( (String) attrs.get(k) ) ) {
6165 //          attribute.setValue( (String) attrs.get(k) );
6166 //          byObject.update(attribute);
6167 //        }
6168 //        attrs.remove(k);
6169 //      }
6170 //      else {
6171 //        // attr only in db.
6172 //        byObject.delete(attribute);
6173 //        attrs.remove(k);
6174 //      }
6175 //    }
6176 //    // now handle entries that were only in memory
6177 //    Map.Entry kv;
6178 //    Iterator it = attrs.entrySet().iterator();
6179 //    while (it.hasNext()) {
6180 //      kv = (Map.Entry) it.next();
6181 //      Attribute attribute = new Attribute(); 
6182 //      attribute.setFieldId( FieldFinder.findFieldIdForAttribute((String) kv.getKey(), true ));
6183 //      attribute.assignGroupUuid(this.getUuid(), this);
6184 //      attribute.setValue( (String) kv.getValue() );
6185 //      byObject.save(attribute);
6186 //    }
6187 //  } // private void _updateAttributes(hs)
6188 
6189 
6190   /**
6191    * @see edu.internet2.middleware.grouper.hibernate.HibGrouperLifecycle#onPostSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
6192    */
6193   @Override
6194   public void onPostSave(HibernateSession hibernateSession) {
6195     super.onPostSave(hibernateSession);
6196         
6197     if (this.isEnabled()) {
6198       GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6199           GroupHooks.METHOD_GROUP_POST_INSERT, HooksGroupBean.class, 
6200           this, Group.class, VetoTypeGrouper.GROUP_POST_INSERT, true, false);
6201       
6202       //do these second so the right object version is set, and dbVersion is ok
6203       GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.GROUP, 
6204           GroupHooks.METHOD_GROUP_POST_COMMIT_INSERT, HooksGroupBean.class, 
6205           this, Group.class);
6206       
6207       InstrumentationThread.addCount(InstrumentationDataBuiltinTypes.API_GROUP_ADD.name());
6208       
6209       PerformanceLogger.performanceTimingGate(GroupSave.PERFORMANCE_LOG_LABEL, "postSaveHook");
6210 
6211     }
6212   }
6213 
6214   /**
6215    * @see edu.internet2.middleware.grouper.hibernate.HibGrouperLifecycle#onPostUpdate(HibernateSession)
6216    */
6217   public void onPostUpdate(HibernateSession hibernateSession) {
6218     
6219     if (this.dbVersionDifferentFields().contains(FIELD_NAME)) {
6220       GrouperSession.callbackGrouperSession(GrouperSession.staticGrouperSession().internal_getRootSession(), new GrouperSessionHandler() {
6221   
6222         /**
6223          * @see edu.internet2.middleware.grouper.misc.GrouperSessionHandler#callback(edu.internet2.middleware.grouper.GrouperSession)
6224          */
6225         public Object callback(GrouperSession rootSession) throws GrouperSessionException {
6226 
6227           // if this is an entity, need to potentially update the subject identifier.
6228           if (Group.this.getTypeOfGroup() == TypeOfGroup.entity) {
6229             String oldPrefix = GrouperUtil.parentStemNameFromName(Group.this.dbVersion().getName()) + ":";
6230             String newPrefix = GrouperUtil.parentStemNameFromName(Group.this.getName()) + ":";
6231             
6232             if (!oldPrefix.equals(newPrefix)) {
6233               String subjectIdentifier = Group.this.getAttributeValueDelegate().retrieveValueString(EntityUtils.entitySubjectIdentifierName());
6234               if (subjectIdentifier != null && subjectIdentifier.startsWith(oldPrefix)) {
6235                 subjectIdentifier = newPrefix + subjectIdentifier.substring(oldPrefix.length());
6236                 Group.this.getAttributeValueDelegate().assignValue(EntityUtils.entitySubjectIdentifierName(), subjectIdentifier);
6237               }
6238             }
6239           }
6240           
6241           // need to potentially update group name in rules
6242           Set<RuleDefinition> definitions = RuleEngine.ruleEngine().getRuleDefinitions();
6243           for (RuleDefinition definition : definitions) {
6244             if (definition.getCheck() != null && definition.getCheck().checkTypeEnum() != null && 
6245                 definition.getCheck().checkTypeEnum().isCheckOwnerTypeGroup(definition) && Group.this.dbVersion().getName().equals(definition.getCheck().getCheckOwnerName())) {
6246               definition.getAttributeAssignType().getAttributeValueDelegate().assignValue(RuleUtils.ruleCheckOwnerNameName(), Group.this.getName());
6247             }
6248             
6249             if (definition.getIfCondition() != null && definition.getIfCondition().ifConditionEnum() != null &&
6250                 definition.getIfCondition().ifConditionEnum().isIfOwnerTypeGroup(definition) && Group.this.dbVersion().getName().equals(definition.getIfCondition().getIfOwnerName())) {
6251               definition.getAttributeAssignType().getAttributeValueDelegate().assignValue(RuleUtils.ruleIfOwnerNameName(), Group.this.getName());
6252             }
6253             
6254             // thenEnumArg0 can be a packed subject string so it may need to be updated...
6255             RuleThenEnum ruleThenEnum = definition.getThen().thenEnum();
6256             if ((ruleThenEnum == RuleThenEnum.assignGroupPrivilegeToGroupId ||
6257                 ruleThenEnum == RuleThenEnum.assignStemPrivilegeToStemId ||
6258                 ruleThenEnum == RuleThenEnum.assignAttributeDefPrivilegeToAttributeDefId) &&
6259                 definition.getThen().getThenEnumArg0().endsWith(Group.this.dbVersion().getName())) {
6260               
6261               String prefix = definition.getThen().getThenEnumArg0().substring(0, definition.getThen().getThenEnumArg0().length() - Group.this.dbVersion().getName().length());
6262               if (prefix.trim().isEmpty() || prefix.trim().endsWith("::")) {
6263                 definition.getAttributeAssignType().getAttributeValueDelegate().assignValue(RuleUtils.ruleThenEnumArg0Name(), prefix + Group.this.getName());
6264               }
6265             }
6266           }
6267           
6268           return null;
6269         }
6270       });
6271     }
6272     
6273     boolean enabling = Group.this.dbVersion() != null && Group.this.isEnabled() && !Group.this.dbVersion().isEnabled();
6274     boolean disabling = Group.this.dbVersion() != null && !Group.this.isEnabled() && Group.this.dbVersion().isEnabled();
6275     
6276     Group oldDbVersion = Group.this.dbVersion();
6277     
6278     super.onPostUpdate(hibernateSession);
6279 
6280     if (disabling) {
6281       GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.GROUP, 
6282           GroupHooks.METHOD_GROUP_POST_COMMIT_DELETE, HooksGroupBean.class, 
6283           this, Group.class);
6284 
6285       GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6286           GroupHooks.METHOD_GROUP_POST_DELETE, HooksGroupBean.class, 
6287           this, Group.class, VetoTypeGrouper.GROUP_POST_DELETE, true, false);
6288       
6289       InstrumentationThread.addCount(InstrumentationDataBuiltinTypes.API_GROUP_DELETE.name());
6290       
6291       if (this.getTypeOfGroup() == TypeOfGroup.entity) {
6292 
6293         //change log into temp table
6294         new ChangeLogEntry(true, ChangeLogTypeBuiltin.ENTITY_DISABLE,
6295             ChangeLogLabels.ENTITY_DISABLE.id.name(),
6296             this.getUuid(), ChangeLogLabels.ENTITY_DISABLE.name.name(),
6297             this.getName(), ChangeLogLabels.ENTITY_DISABLE.parentStemId.name(), this.getParentUuid(),
6298             ChangeLogLabels.ENTITY_DISABLE.displayName.name(), this.getDisplayName(),
6299             ChangeLogLabels.ENTITY_DISABLE.description.name(), this.getDescription()).save();
6300       } else {
6301  
6302         //change log into temp table
6303         new ChangeLogEntry(true, ChangeLogTypeBuiltin.GROUP_DISABLE,
6304             ChangeLogLabels.GROUP_DISABLE.id.name(),
6305             this.getUuid(), ChangeLogLabels.GROUP_DISABLE.name.name(),
6306             this.getName(), ChangeLogLabels.GROUP_DISABLE.parentStemId.name(), this.getParentUuid(),
6307             ChangeLogLabels.GROUP_DISABLE.displayName.name(), this.getDisplayName(),
6308             ChangeLogLabels.GROUP_DISABLE.description.name(), this.getDescription(),
6309             ChangeLogLabels.GROUP_DISABLE.idIndex.name(), "" + this.getIdIndex()).save();
6310       }
6311       
6312       // these queries are best run post update to make sure internal_isEnabledUsingTimestamps is calculating correctly
6313       Set<Membership> membershipsToDisable = GrouperDAOFactory.getFactory().getMembership().findAllByGroupOwnerAndDepth(this.uuid, 0, true);
6314       membershipsToDisable.addAll(GrouperDAOFactory.getFactory().getMembership().findAllByMemberAndDepth(this.toMember().getUuid(), 0, true));
6315       for (Membership membershipToDisable : membershipsToDisable) {
6316         if (this.uuid.equals(membershipToDisable.getOwnerGroupId())) {
6317           // if this is an admin priv, skip
6318           if (membershipToDisable.getField().equals(AccessPrivilege.ADMIN.getField())) {
6319             continue;
6320           }
6321           
6322           membershipToDisable.setOwnerGroup(this);
6323         }
6324         membershipToDisable.setEnabled(false);
6325         GrouperDAOFactory.getFactory().getMembership().update(membershipToDisable);
6326       }
6327       
6328       // now lets deal with attribute assignments
6329       Set<AttributeAssign> assignmentsToDisable = new LinkedHashSet<AttributeAssign>();
6330       assignmentsToDisable.addAll(GrouperDAOFactory.getFactory().getAttributeAssign().findByOwnerGroupId(this.uuid));
6331       assignmentsToDisable.addAll(GrouperDAOFactory.getFactory().getAttributeAssign().findByOwnerMemberId(this.toMember().getUuid()));
6332       
6333       for (AttributeAssign assignmentToDisable : assignmentsToDisable) {
6334         assignmentToDisable.setEnabled(false);
6335         assignmentToDisable.saveOrUpdate(false);
6336       }
6337             
6338       if (this.getTypeOfGroup() == TypeOfGroup.entity) {
6339         new ChangeLogEntry(true, ChangeLogTypeBuiltin.ENTITY_DELETE,
6340             ChangeLogLabels.ENTITY_DELETE.id.name(),
6341             this.getUuid(), ChangeLogLabels.ENTITY_DELETE.name.name(),
6342             this.getName(), ChangeLogLabels.ENTITY_DELETE.parentStemId.name(), this.getParentUuid(),
6343             ChangeLogLabels.ENTITY_DELETE.displayName.name(), this.getDisplayName(),
6344             ChangeLogLabels.ENTITY_DELETE.description.name(), this.getDescription()).save();
6345       } else {
6346         new ChangeLogEntry(true, ChangeLogTypeBuiltin.GROUP_DELETE,
6347             ChangeLogLabels.GROUP_DELETE.id.name(),
6348             this.getUuid(), ChangeLogLabels.GROUP_DELETE.name.name(),
6349             this.getName(), ChangeLogLabels.GROUP_DELETE.parentStemId.name(), this.getParentUuid(),
6350             ChangeLogLabels.GROUP_DELETE.displayName.name(), this.getDisplayName(),
6351             ChangeLogLabels.GROUP_DELETE.description.name(), this.getDescription(),
6352             ChangeLogLabels.GROUP_DELETE.idIndex.name(), "" + this.getIdIndex()).save();
6353       }
6354     } else if (enabling) {
6355       GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6356           GroupHooks.METHOD_GROUP_POST_INSERT, HooksGroupBean.class, 
6357           this, Group.class, VetoTypeGrouper.GROUP_POST_INSERT, true, false);
6358       
6359       //do these second so the right object version is set, and dbVersion is ok
6360       GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.GROUP, 
6361           GroupHooks.METHOD_GROUP_POST_COMMIT_INSERT, HooksGroupBean.class, 
6362           this, Group.class);
6363       
6364       InstrumentationThread.addCount(InstrumentationDataBuiltinTypes.API_GROUP_ADD.name());
6365             
6366       if (this.getTypeOfGroup() == TypeOfGroup.entity) {
6367 
6368         //change log into temp table
6369         new ChangeLogEntry(true, ChangeLogTypeBuiltin.ENTITY_ENABLE,
6370             ChangeLogLabels.ENTITY_ENABLE.id.name(),
6371             this.getUuid(), ChangeLogLabels.ENTITY_ENABLE.name.name(),
6372             this.getName(), ChangeLogLabels.ENTITY_ENABLE.parentStemId.name(), this.getParentUuid(),
6373             ChangeLogLabels.ENTITY_ENABLE.displayName.name(), this.getDisplayName(),
6374             ChangeLogLabels.ENTITY_ENABLE.description.name(), this.getDescription()).save();
6375 
6376         new ChangeLogEntry(true, ChangeLogTypeBuiltin.ENTITY_ADD,
6377             ChangeLogLabels.ENTITY_ADD.id.name(),
6378             this.getUuid(), ChangeLogLabels.ENTITY_ADD.name.name(),
6379             this.getName(), ChangeLogLabels.ENTITY_ADD.parentStemId.name(), this.getParentUuid(),
6380             ChangeLogLabels.ENTITY_ADD.displayName.name(), this.getDisplayName(),
6381             ChangeLogLabels.ENTITY_ADD.description.name(), this.getDescription()).save();
6382 
6383       } else {
6384    
6385         //change log into temp table
6386         new ChangeLogEntry(true, ChangeLogTypeBuiltin.GROUP_ENABLE,
6387             ChangeLogLabels.GROUP_ENABLE.id.name(),
6388             this.getUuid(), ChangeLogLabels.GROUP_ENABLE.name.name(),
6389             this.getName(), ChangeLogLabels.GROUP_ENABLE.parentStemId.name(), this.getParentUuid(),
6390             ChangeLogLabels.GROUP_ENABLE.displayName.name(), this.getDisplayName(),
6391             ChangeLogLabels.GROUP_ENABLE.description.name(), this.getDescription(),
6392             ChangeLogLabels.GROUP_ENABLE.idIndex.name(), "" + this.getIdIndex()).save();
6393    
6394         new ChangeLogEntry(true, ChangeLogTypeBuiltin.GROUP_ADD,
6395             ChangeLogLabels.GROUP_ADD.id.name(),
6396             this.getUuid(), ChangeLogLabels.GROUP_ADD.name.name(),
6397             this.getName(), ChangeLogLabels.GROUP_ADD.parentStemId.name(), this.getParentUuid(),
6398             ChangeLogLabels.GROUP_ADD.displayName.name(), this.getDisplayName(),
6399             ChangeLogLabels.GROUP_ADD.description.name(), this.getDescription(),
6400             ChangeLogLabels.GROUP_ADD.idIndex.name(), "" + this.getIdIndex()).save();
6401    
6402       }
6403       
6404       // these queries are best run post update to make sure internal_isEnabledUsingTimestamps is calculating correctly
6405       Set<Membership> membershipsToEnable = GrouperDAOFactory.getFactory().getMembership().findAllByGroupOwnerAndDepth(this.uuid, 0, false);
6406       membershipsToEnable.addAll(GrouperDAOFactory.getFactory().getMembership().findAllByMemberAndDepth(this.toMember().getUuid(), 0, false));
6407       for (Membership membershipToEnable : membershipsToEnable) {
6408         if (this.uuid.equals(membershipToEnable.getOwnerGroupId())) {
6409           // if this is an admin priv, skip
6410           if (membershipToEnable.getField().equals(AccessPrivilege.ADMIN.getField())) {
6411             continue;
6412           }
6413           
6414           membershipToEnable.setOwnerGroup(this);
6415         }
6416         
6417         membershipToEnable.setEnabled(membershipToEnable.internal_isEnabledUsingTimestamps());
6418         
6419         if (membershipToEnable.isEnabled()) {
6420           GrouperDAOFactory.getFactory().getMembership().update(membershipToEnable);
6421         }
6422       }
6423       
6424       // now lets deal with attribute assignments
6425       Set<AttributeAssign> assignmentsToEnable = new LinkedHashSet<AttributeAssign>();
6426       assignmentsToEnable.addAll(GrouperDAOFactory.getFactory().getAttributeAssign().findByOwnerGroupId(this.uuid));
6427       assignmentsToEnable.addAll(GrouperDAOFactory.getFactory().getAttributeAssign().findByOwnerMemberId(this.toMember().getUuid()));
6428       
6429       for (AttributeAssign assignmentToEnable : assignmentsToEnable) {
6430         assignmentToEnable.setEnabled(assignmentToEnable.internal_isEnabledUsingTimestamps());
6431         
6432         if (assignmentToEnable.isEnabled()) {
6433           assignmentToEnable.saveOrUpdate(false);
6434         }
6435       }
6436     } else {
6437       GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.GROUP, 
6438           GroupHooks.METHOD_GROUP_POST_COMMIT_UPDATE, HooksGroupBean.class, 
6439           this, Group.class);
6440 
6441       GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6442           GroupHooks.METHOD_GROUP_POST_UPDATE, HooksGroupBean.class, 
6443           this, Group.class, VetoTypeGrouper.GROUP_POST_UPDATE, true, false); 
6444       
6445       if (this.getTypeOfGroup() == TypeOfGroup.entity) {
6446         //change log into temp table
6447         ChangeLogEntry.saveTempUpdates(ChangeLogTypeBuiltin.ENTITY_UPDATE,
6448             this, oldDbVersion,
6449             GrouperUtil.toList(ChangeLogLabels.ENTITY_UPDATE.id.name(),this.getUuid(),
6450                 ChangeLogLabels.ENTITY_UPDATE.name.name(), this.getName(),
6451                 ChangeLogLabels.ENTITY_UPDATE.parentStemId.name(), this.getParentUuid(),
6452                 ChangeLogLabels.ENTITY_UPDATE.displayName.name(), this.getDisplayName(),
6453                 ChangeLogLabels.ENTITY_UPDATE.description.name(), this.getDescription()),
6454             GrouperUtil.toList(FIELD_NAME, FIELD_PARENT_UUID, FIELD_DESCRIPTION, FIELD_DISPLAY_EXTENSION),
6455             GrouperUtil.toList(ChangeLogLabels.GROUP_UPDATE.name.name(),
6456                 ChangeLogLabels.ENTITY_UPDATE.parentStemId.name(),
6457                 ChangeLogLabels.ENTITY_UPDATE.description.name(),
6458                 ChangeLogLabels.ENTITY_UPDATE.displayExtension.name()));
6459       } else {
6460         //change log into temp table
6461         ChangeLogEntry.saveTempUpdates(ChangeLogTypeBuiltin.GROUP_UPDATE,
6462             this, oldDbVersion,
6463             GrouperUtil.toList(ChangeLogLabels.GROUP_UPDATE.id.name(),this.getUuid(),
6464                 ChangeLogLabels.GROUP_UPDATE.name.name(), this.getName(),
6465                 ChangeLogLabels.GROUP_UPDATE.parentStemId.name(), this.getParentUuid(),
6466                 ChangeLogLabels.GROUP_UPDATE.displayName.name(), this.getDisplayName(),
6467                 ChangeLogLabels.GROUP_UPDATE.description.name(), this.getDescription()),
6468             GrouperUtil.toList(FIELD_NAME, FIELD_PARENT_UUID, FIELD_DESCRIPTION, FIELD_DISPLAY_EXTENSION, FIELD_DISPLAY_NAME),
6469             GrouperUtil.toList(ChangeLogLabels.GROUP_UPDATE.name.name(),
6470                 ChangeLogLabels.GROUP_UPDATE.parentStemId.name(),
6471                 ChangeLogLabels.GROUP_UPDATE.description.name(),
6472                 ChangeLogLabels.GROUP_UPDATE.displayExtension.name(),
6473                 ChangeLogLabels.GROUP_UPDATE.displayName.name()
6474                 ));
6475 
6476       }
6477     }
6478 
6479     // update member table
6480     this.toMember().updateMemberAttributes(new GrouperSubject(this), true);
6481   }
6482 
6483   /**
6484    * when the last member has changed
6485    */
6486   private Long lastMembershipChangeDb;
6487   
6488   /**
6489    * when the last member has changed
6490    * @return when
6491    */
6492   public Long getLastMembershipChangeDb() {
6493     return this.lastMembershipChangeDb;
6494   }
6495   
6496   /**
6497    * when the last member has changed
6498    * @param theMembershipLastChange
6499    */
6500   public void setLastMembershipChangeDb(Long theMembershipLastChange) {
6501     this.lastMembershipChangeDb = theMembershipLastChange;
6502   }
6503   
6504   /**
6505    * when the last member has changed
6506    * @return the membership last change timestamp
6507    */
6508   public Timestamp getLastMembershipChange() {
6509     return this.lastMembershipChangeDb == null ? null : new Timestamp(this.lastMembershipChangeDb);
6510   }
6511   
6512   /**
6513    * when the last immediate member has changed
6514    */
6515   private Long lastImmediateMembershipChangeDb;
6516   
6517   /**
6518    * when the last immediate member has changed
6519    * @return when
6520    */
6521   public Long getLastImmediateMembershipChangeDb() {
6522     return this.lastImmediateMembershipChangeDb;
6523   }
6524   
6525   /**
6526    * when the last immediate member has changed
6527    * @param theImmediateMembershipLastChange
6528    */
6529   public void setLastImmediateMembershipChangeDb(Long theImmediateMembershipLastChange) {
6530     this.lastImmediateMembershipChangeDb = theImmediateMembershipLastChange;
6531   }
6532   
6533   /**
6534    * when the last immediate member has changed
6535    * @return the immediate membership last change timestamp
6536    */
6537   public Timestamp getLastImmediateMembershipChange() {
6538     return this.lastImmediateMembershipChangeDb == null ? null : new Timestamp(this.lastImmediateMembershipChangeDb);
6539   }
6540   
6541   
6542   /**
6543    * Returns the alternate name for the group.  Used by hibernate.
6544    * @return the alternate name
6545    */
6546   public String getAlternateNameDb() {
6547     return this.alternateNameDb;
6548   }
6549 
6550   /**
6551    * Returns the alternate name for the group.  If multiple, this returns the first one
6552    * @return the alternate name
6553    */
6554   public String getAlternateName() {
6555     return this.alternateNameDb;
6556   }
6557 
6558   /**
6559    * Set the group's alternate name  Used by hibernate.
6560    * @param alternateName
6561    */
6562   public void setAlternateNameDb(String alternateName) {
6563     this.alternateNameDb = alternateName;
6564     this.alternateNames = null;
6565   }
6566   
6567   /** alternate names */
6568   private Set<String> alternateNames = null;
6569   
6570   /**
6571    * Returns the alternate names for the group.  Only one alternate name is supported
6572    * currently, so a Set of size 0 or 1 will be returned.
6573    * @return Set of alternate names.
6574    */
6575   public Set<String> getAlternateNames() {
6576     
6577     //lazy load this set
6578     if (this.alternateNames == null) {
6579       this.alternateNames = new LinkedHashSet<String>();
6580       if (!StringUtils.isBlank(this.alternateNameDb)) {
6581         this.alternateNames.add(this.alternateNameDb);
6582       }
6583       this.alternateNames = Collections.unmodifiableSet(this.alternateNames);
6584     }
6585     return this.alternateNames;
6586   }
6587 
6588   /**
6589    * Add an alternate name for this group.  Only one alternate name is supported
6590    * currently, so this will replace any existing alternate name.
6591    * This won't get saved until you call store().
6592    * @param alternateName
6593    */
6594   public void addAlternateName(String alternateName) {
6595     internal_addAlternateName(alternateName, true);
6596   }
6597   
6598   /**
6599    * Add an alternate name for this group.  Only one alternate name is supported
6600    * currently, so this will replace any existing alternate name.
6601    * This won't get saved until you call store().
6602    * @param alternateName
6603    * @param validateName
6604    */
6605   protected void internal_addAlternateName(String alternateName, boolean validateName) {
6606     
6607     if (validateName) {
6608       GrouperValidator v = AddAlternateGroupNameValidator.validate(alternateName);
6609     
6610       if (v.isInvalid()) {
6611         throw new GroupModifyException(v.getErrorMessage() + ": " + alternateName);
6612       }
6613     }
6614 
6615     this.alternateNameDb = alternateName;
6616   }
6617   
6618   /**
6619    * Delete the specified alternate name.  This won't get saved until you call store().
6620    * @param alternateName
6621    * @return false if the group does not have the specified alternate name
6622    */
6623   public boolean deleteAlternateName(String alternateName) {
6624     if (alternateName.equals(this.alternateNameDb)) {
6625       this.alternateNameDb = null;
6626       return true;
6627     }
6628     
6629     return false;
6630   }
6631   
6632   
6633   /**
6634    * @see edu.internet2.middleware.grouper.GrouperAPI#onPostDelete(edu.internet2.middleware.grouper.hibernate.HibernateSession)
6635    */
6636   @Override
6637   public void onPostDelete(HibernateSession hibernateSession) {
6638     super.onPostDelete(hibernateSession);
6639 
6640     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.GROUP, 
6641         GroupHooks.METHOD_GROUP_POST_COMMIT_DELETE, HooksGroupBean.class, 
6642         this, Group.class);
6643 
6644     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6645         GroupHooks.METHOD_GROUP_POST_DELETE, HooksGroupBean.class, 
6646         this, Group.class, VetoTypeGrouper.GROUP_POST_DELETE, false, true);
6647 
6648     InstrumentationThread.addCount(InstrumentationDataBuiltinTypes.API_GROUP_DELETE.name());
6649   }
6650 
6651   /**
6652    * 
6653    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
6654    */
6655   @Override
6656   public void onPreSave(HibernateSession hibernateSession) {
6657     super.onPreSave(hibernateSession);
6658     
6659     this.internal_setModifiedIfNeeded();
6660     
6661     if (this.idIndex == null) {
6662       this.idIndex = TableIndex.reserveId(TableIndexType.group);
6663     }
6664     
6665     if (!this.isEnabled()) {
6666       // This needs to be fixed.  Need to still add to temp change log so it can be added to pit but not to the real change log?
6667       throw new RuntimeException("Group must be enabled on creation for now");
6668     }
6669     
6670     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6671         GroupHooks.METHOD_GROUP_PRE_INSERT, HooksGroupBean.class, 
6672         this, Group.class, VetoTypeGrouper.GROUP_PRE_INSERT, false, false);
6673 
6674     PerformanceLogger.performanceTimingGate(GroupSave.PERFORMANCE_LOG_LABEL, "preSaveHook");
6675 
6676     if (this.getTypeOfGroup() == TypeOfGroup.entity) {
6677 
6678       //change log into temp table
6679       new ChangeLogEntry(true, ChangeLogTypeBuiltin.ENTITY_ADD, 
6680           ChangeLogLabels.ENTITY_ADD.id.name(), 
6681           this.getUuid(), ChangeLogLabels.ENTITY_ADD.name.name(), 
6682           this.getName(), ChangeLogLabels.ENTITY_ADD.parentStemId.name(), this.getParentUuid(),
6683           ChangeLogLabels.ENTITY_ADD.displayName.name(), this.getDisplayName(),
6684           ChangeLogLabels.ENTITY_ADD.description.name(), this.getDescription()).save();
6685 
6686     } else {
6687 
6688       //change log into temp table
6689       new ChangeLogEntry(true, ChangeLogTypeBuiltin.GROUP_ADD, 
6690           ChangeLogLabels.GROUP_ADD.id.name(), 
6691           this.getUuid(), ChangeLogLabels.GROUP_ADD.name.name(), 
6692           this.getName(), ChangeLogLabels.GROUP_ADD.parentStemId.name(), this.getParentUuid(),
6693           ChangeLogLabels.GROUP_ADD.displayName.name(), this.getDisplayName(),
6694           ChangeLogLabels.GROUP_ADD.description.name(), this.getDescription(),
6695           ChangeLogLabels.GROUP_ADD.idIndex.name(), "" + this.getIdIndex()).save();
6696 
6697     }
6698     PerformanceLogger.performanceTimingGate(GroupSave.PERFORMANCE_LOG_LABEL, "changeLog");
6699 
6700   }
6701 
6702   /**
6703    * 
6704    * @see edu.internet2.middleware.grouper.GrouperAPI#onSave(org.hibernate.Session)
6705    */
6706   public boolean onSave(Session hs) throws  CallbackException {
6707     GrouperDAOFactory.getFactory().getGroup().putInExistsCache( this.getUuid(), true );
6708         return Lifecycle.NO_VETO;
6709   }
6710   
6711   /**
6712    * @see edu.internet2.middleware.grouper.GrouperAPI#onUpdate(org.hibernate.Session)
6713    */
6714   @Override
6715   public boolean onUpdate(Session s) throws CallbackException {
6716     GroupFinder.groupCacheRemove(this);
6717     return super.onUpdate(s);
6718   }
6719 
6720   /**
6721    * 
6722    * @param attributes
6723    * @deprecated
6724    */
6725   public void setAttributes(Map<String, String> attributes) {
6726     
6727     attributes = GrouperUtil.nonNull(attributes);
6728     
6729     //make a copy of existing attributes
6730     Set<String> existingAttributeNames = 
6731       new HashSet<String>(this.getAttributesMap(false).keySet());
6732     
6733     //add or change
6734     for (String key : attributes.keySet()) {
6735       this.setAttribute(key, attributes.get(key));
6736       existingAttributeNames.remove(key);
6737     }
6738     
6739     //remove ones not in there
6740     for (String key : existingAttributeNames) {
6741       this.deleteAttribute(key);
6742     }
6743   }
6744 
6745   /**
6746    * @param createTime 
6747    * @since   1.2.0
6748    */
6749   public void setCreateTimeLong(long createTime) {
6750     this.createTime = createTime;
6751 
6752   }
6753 
6754   /**
6755    * @param creatorUUID 
6756    * @since   1.2.0
6757    */
6758   public void setCreatorUuid(String creatorUUID) {
6759     this.creatorUUID = creatorUUID;
6760   
6761   }
6762 
6763   /**
6764    * @param modifierUUID 
6765    * @since   1.2.0
6766    */
6767   public void setModifierUuid(String modifierUUID) {
6768     this.modifierUUID = modifierUUID;
6769 
6770   }
6771 
6772   /**
6773    * @param modifyTime 
6774    * @since   1.2.0
6775    */
6776   public void setModifyTimeLong(long modifyTime) {
6777     this.modifyTime = modifyTime;
6778 
6779   }
6780 
6781   /**
6782    * @param parentUUID 
6783    * @since   1.2.0
6784    */
6785   public void setParentUuid(String parentUUID) {
6786     this.parentUuid = parentUUID;
6787 
6788   }
6789 
6790   /**
6791    * @param types 
6792    * @since   1.2.0
6793    */
6794   public void setTypes(Set<GroupType> types) {
6795     Map<String, AttributeDefName> results = new LinkedHashMap<String, AttributeDefName>();
6796     
6797     if (types != null) {
6798       for (GroupType groupType : types) {
6799         results.put(groupType.getName(), groupType.getAttributeDefName());
6800       }
6801     }
6802     
6803     this.types = new LinkedHashMap<String, AttributeDefName>(results);
6804     this.typeAssignments = null;  // assignments may not exist in db right now
6805   }
6806   
6807   /**
6808    * @return type assignments
6809    */
6810   public Map<String, AttributeAssign> internal_getGroupTypeAssignments() {
6811     if (this.typeAssignments == null) {
6812       this.types = null;
6813       this.getTypesDb();
6814     }
6815     
6816     return this.typeAssignments;
6817   }
6818 
6819   /**
6820    * @param uuid 
6821    * @since   1.2.0
6822    */
6823   public void setUuid(String uuid) {
6824     this.uuid = uuid;
6825 
6826   }
6827 
6828   /**
6829    * @return string
6830    * @since   1.2.0
6831    */
6832   public String toStringDb() {
6833     return new ToStringBuilder(this)
6834       .append( "attributes",   this.getAttributesDb()   )
6835       .append( "createTime",   this.getCreateTimeLong()   )
6836       .append( "creatorUuid",  this.getCreatorUuid()  )
6837       .append( "modifierUuid", this.getModifierUuid() )
6838       .append( "modifyTime",   this.getModifyTime()   )
6839       .append( "uuid",    this.getUuid()         )
6840       .append( "parentUuid",   this.getParentUuid()   )
6841       .append( "types",        this.getTypesDb()        )
6842       .toString();
6843   } // public String toString()
6844 
6845   /**
6846    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreDelete(edu.internet2.middleware.grouper.hibernate.HibernateSession)
6847    */
6848   @Override
6849   public void onPreDelete(HibernateSession hibernateSession) {
6850     super.onPreDelete(hibernateSession);
6851 
6852     // always do this even if group was disabled?  just to make sure target cleans up properly in case they were handling disabled groups differently?
6853     // and to make sure pit is cleaned up?
6854     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6855         GroupHooks.METHOD_GROUP_PRE_DELETE, HooksGroupBean.class, 
6856         this, Group.class, VetoTypeGrouper.GROUP_PRE_DELETE, false, false);
6857 
6858     if (this.getTypeOfGroup() == TypeOfGroup.entity) {
6859 
6860       //change log into temp table
6861       new ChangeLogEntry(true, ChangeLogTypeBuiltin.ENTITY_DELETE, 
6862           ChangeLogLabels.ENTITY_DELETE.id.name(), 
6863           this.getUuid(), ChangeLogLabels.ENTITY_DELETE.name.name(), 
6864           this.getName(), ChangeLogLabels.ENTITY_DELETE.parentStemId.name(), this.getParentUuid(),
6865           ChangeLogLabels.ENTITY_DELETE.displayName.name(), this.getDisplayName(),
6866           ChangeLogLabels.ENTITY_DELETE.description.name(), this.getDescription()).save();
6867 
6868     } else {
6869 
6870       //change log into temp table
6871       new ChangeLogEntry(true, ChangeLogTypeBuiltin.GROUP_DELETE, 
6872           ChangeLogLabels.GROUP_DELETE.id.name(), 
6873           this.getUuid(), ChangeLogLabels.GROUP_DELETE.name.name(), 
6874           this.getName(), ChangeLogLabels.GROUP_DELETE.parentStemId.name(), this.getParentUuid(),
6875           ChangeLogLabels.GROUP_DELETE.displayName.name(), this.getDisplayName(),
6876           ChangeLogLabels.GROUP_DELETE.description.name(), this.getDescription(),
6877           ChangeLogLabels.GROUP_DELETE.idIndex.name(), "" + this.getIdIndex()).save();
6878 
6879     }
6880   }
6881   
6882   /**
6883    * @param fieldName 
6884    * @return  the value
6885    * @see edu.internet2.middleware.grouper.GrouperAPI#fieldValue(java.lang.String)
6886    */
6887   public Object fieldValue(String fieldName) {
6888     
6889     return super.fieldValue(fieldName);
6890   }
6891 
6892   /**
6893    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreUpdate(edu.internet2.middleware.grouper.hibernate.HibernateSession)
6894    */
6895   @Override
6896   public void onPreUpdate(HibernateSession hibernateSession) {
6897     
6898     super.onPreUpdate(hibernateSession);
6899     
6900     //if the type of group was changed, and it went to or from entity, that is bad
6901     if (this.dbVersionDifferentFields().contains(FIELD_TYPE_OF_GROUP) 
6902         && this.dbVersion != null && (this.typeOfGroup == TypeOfGroup.entity || ((Group)this.dbVersion).getTypeOfGroup() == TypeOfGroup.entity)) {
6903       throw new RuntimeException("Cannot change typeOfGroup, you need to delete and create the object");
6904     }
6905     
6906     // If the group name is changing, verify that the new name is not in use.
6907     // (The new name could be an alternate name).
6908     if (this.dbVersionDifferentFields().contains(FIELD_NAME)) {
6909       Group check = GrouperDAOFactory.getFactory().getGroup().findByName(this.getNameDb(), false);
6910       if (check != null && 
6911           (!check.getUuid().equals(this.getUuid()) || 
6912               (this.getAlternateNameDb() != null && 
6913                   this.getAlternateNameDb().equals(this.getNameDb())))) {
6914         throw new GroupModifyAlreadyExistsException("Group with name " + this.getNameDb() + " already exists.");
6915       }
6916     }
6917     
6918     // If the alternate name is changing, do the following check...
6919     // If the group name is not changing OR
6920     // if the group name is changing and the alternate name is not the old group name, THEN
6921     // we need to verify the alternate name isn't already taken.
6922     if (this.dbVersionDifferentFields().contains(FIELD_ALTERNATE_NAME_DB) &&
6923         this.getAlternateNameDb() != null) {
6924       
6925       String oldName = this.dbVersion().getNameDb();
6926       if (!this.dbVersionDifferentFields().contains(FIELD_NAME) || 
6927           (this.dbVersionDifferentFields().contains(FIELD_NAME) && 
6928               !oldName.equals(this.getAlternateNameDb()))) {
6929         Group check = GrouperDAOFactory.getFactory().getGroup().findByName(this.getAlternateNameDb(), false);
6930         if (check != null) {
6931           throw new GroupModifyAlreadyExistsException("Group with name " + this.getAlternateNameDb() + " already exists.");
6932         }
6933       }
6934     }
6935     
6936     this.internal_setModifiedIfNeeded();
6937     
6938     boolean enabling = Group.this.dbVersion() != null && Group.this.isEnabled() && !Group.this.dbVersion().isEnabled();
6939     boolean disabling = Group.this.dbVersion() != null && !Group.this.isEnabled() && Group.this.dbVersion().isEnabled();
6940     
6941     if (enabling) {
6942       GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6943           GroupHooks.METHOD_GROUP_PRE_INSERT, HooksGroupBean.class, 
6944           this, Group.class, VetoTypeGrouper.GROUP_PRE_INSERT, false, false);
6945     } else if (disabling) {
6946       GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6947           GroupHooks.METHOD_GROUP_PRE_DELETE, HooksGroupBean.class, 
6948           this, Group.class, VetoTypeGrouper.GROUP_PRE_DELETE, false, false);
6949     } else {
6950       GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.GROUP, 
6951           GroupHooks.METHOD_GROUP_PRE_UPDATE, HooksGroupBean.class, 
6952           this, Group.class, VetoTypeGrouper.GROUP_PRE_UPDATE, false, false);
6953     }
6954   }
6955 
6956   /**
6957    * @see java.lang.Comparable#compareTo(java.lang.Object)
6958    */
6959   public int compareTo(Object that) {
6960     if (that==null) {
6961       return 1;
6962     }
6963     if (!(that instanceof GrouperObject)) {
6964       return 1;
6965     }
6966     String thisName = StringUtils.defaultString(this.getName());
6967     String thatName = StringUtils.defaultString(((GrouperObject)that).getName());
6968     return thisName.compareTo(thatName);
6969   }
6970 
6971   /**
6972    * add or correct the includes/excludes group math structure for this group (as overall or system
6973    * or record if named correctly)
6974    * @param grouperSession
6975    * @param isIncludeExcludes 
6976    */
6977   public void manageIncludesExcludesRequiredGroups(GrouperSession grouperSession, boolean isIncludeExcludes) {
6978     this.manageIncludesExcludesRequiredGroups(grouperSession, isIncludeExcludes, new LinkedHashSet<Group>());
6979   }
6980 
6981   /**
6982    * add or correct the includes/excludes group math structure for this group (as overall or system
6983    * or record if named correctly)
6984    * @param grouperSession
6985    * @param isIncludeExcludes 
6986    * @param andGroup groups (like activeEmployee) which the user must also be in
6987    */
6988   public void manageIncludesExcludesRequiredGroups(GrouperSession grouperSession, boolean isIncludeExcludes, Group andGroup) {
6989     
6990     this.manageIncludesExcludesRequiredGroups(grouperSession, isIncludeExcludes, 
6991         andGroup == null ? new HashSet<Group>() : GrouperUtil.toSet(andGroup));
6992   }
6993 
6994   /**
6995    * add or correct the includes/excludes group math structure for this group (as overall or system
6996    * or record if named correctly)
6997    * @param grouperSession
6998    * @param isIncludeExcludes 
6999    * @param andGroups groups (like activeEmployee) which the user must also be in
7000    */
7001   public void manageIncludesExcludesRequiredGroups(GrouperSession grouperSession, boolean isIncludeExcludes, Set<Group> andGroups) {
7002     
7003     GroupTypeTupleIncludeExcludeHook.manageIncludesExcludesAndGroups(this, isIncludeExcludes, andGroups,
7004         "from manageIncludesExclude() method in Group class: " + this.getExtension());
7005   }
7006 
7007   /**
7008    * Get group displayName for hibernate.
7009    * <pre class="eg">
7010    * String displayName = g.getDisplayName();
7011    * </pre>
7012    * @return  Group displayName.
7013    * @throws  GrouperException
7014    */
7015   public String getDisplayNameDb() 
7016     throws  GrouperException
7017   {
7018     return this.displayName;
7019   } // public String getDisplayName()
7020 
7021   /**
7022    * Get group name for hibernate.
7023    * @return  Group name db.
7024    * @throws  GrouperException
7025    */
7026   public String getNameDb() {
7027     return this.name;
7028   }
7029 
7030   /**
7031    * This is really only for hibernate
7032    * @param value new display name
7033    */
7034   public void setDisplayNameDb(String value) {
7035     this.displayName = value;
7036   }
7037 
7038   /**
7039    * Set group <i>name</i>.  This should not be called
7040    * @param   value   Set <i>extension</i> to this value.
7041    */
7042   public void setNameDb(String value) {
7043     this.name = value;
7044   }
7045 
7046   /**
7047    * Move this group to another Stem.  If you would like to specify options for the move, 
7048    * use GroupMove instead.  This will use the default options.
7049    * @param stem 
7050    * @throws GroupModifyException 
7051    * @throws InsufficientPrivilegeException 
7052    */
7053   public void move(Stem stem) throws GroupModifyException,
7054       InsufficientPrivilegeException {
7055     
7056     new GroupMove(this, stem).save();
7057   }
7058   
7059   /**
7060    * 
7061    * @param stem
7062    * @param assignAlternateName
7063    */
7064   protected void internal_move(final Stem stem, final boolean assignAlternateName) {
7065 
7066     HibernateSession.callbackHibernateSession(
7067         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
7068         new HibernateHandler() {
7069 
7070           public Object callback(HibernateHandlerBean hibernateHandlerBean)
7071               throws GrouperDAOException {
7072 
7073             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
7074 
7075             GrouperSession.validate(GrouperSession.staticGrouperSession());
7076             
7077             // cannot move to the root stem
7078             if (stem.isRootStem()) {
7079               throw new GroupModifyException("Cannot move group to the root stem.");
7080             }
7081             
7082             // verify that the subject has admin privileges to the group
7083             if (!PrivilegeHelper.canAdmin(GrouperSession.staticGrouperSession(), Group.this,
7084                 GrouperSession.staticGrouperSession().getSubject())) {
7085               throw new InsufficientPrivilegeException(E.CANNOT_ADMIN);
7086             }
7087             
7088             // verify that the subject can create groups in the stem where the group will be moved to.
7089             if (!PrivilegeHelper.canCreate(GrouperSession.staticGrouperSession(), stem,
7090                 GrouperSession.staticGrouperSession().getSubject())) {
7091               throw new InsufficientPrivilegeException(E.CANNOT_CREATE);
7092             }
7093             
7094             // if moving to the same stem, just return.
7095             if (stem.getUuid().equals(Group.this.getParentUuid())) {
7096               return null;
7097             }
7098             
7099             String oldName = Group.this.dbVersion().getNameDb();
7100             
7101             Group.this.setParentUuid(stem.getUuid());
7102             Group.this.setNameDb(stem.getName() + Stem.DELIM + Group.this.getExtension());
7103             Group.this.setDisplayNameDb(stem.getDisplayName() + Stem.DELIM 
7104                 + Group.this.getDisplayExtension());
7105             
7106             if (assignAlternateName) {
7107               Group.this.internal_addAlternateName(oldName, false);
7108             }
7109             
7110             GrouperDAOFactory.getFactory().getGroup().update(Group.this);
7111             
7112             //if not a smaller operation of a larger auditable call
7113             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
7114 
7115               AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_MOVE,
7116                   "groupId", Group.this.getUuid(), "oldGroupName", 
7117                   oldName, "newGroupName", Group.this.getName(), "newStemId",
7118                   stem.getUuid(), 
7119                   "assignAlternateName", assignAlternateName ? "T" : "F");
7120               auditEntry.setDescription("Move group " + oldName + " to name: " + Group.this.getName()
7121                   + ", assignAlternateName? " + (assignAlternateName ? "T" : "F")); 
7122               auditEntry.saveOrUpdate(true);
7123             }
7124 
7125             
7126             return null;
7127           }
7128         });
7129   }
7130   
7131   /**
7132    * 
7133    * @param stem
7134    * @param privilegesOfGroup
7135    * @param groupAsPrivilege
7136    * @param listMembersOfGroup
7137    * @param listGroupAsMember
7138    * @param attributes
7139    * @param composite
7140    * @param addDefaultGroupPrivileges
7141    * @param checkSecurity true to check that user can do this operation, false to ignore
7142    * @param extension
7143    * @param displayExtension
7144    * @return group
7145    * @throws GroupAddException
7146    * @throws InsufficientPrivilegeException
7147    */
7148   protected Group internal_copy(final Stem stem, final boolean privilegesOfGroup,
7149       final boolean groupAsPrivilege, final boolean listMembersOfGroup,
7150       final boolean listGroupAsMember, final boolean attributes, final boolean composite,
7151       final boolean addDefaultGroupPrivileges, final boolean checkSecurity, 
7152       final String extension, final String displayExtension)
7153       throws GroupAddException, InsufficientPrivilegeException {
7154     
7155     return (Group)HibernateSession.callbackHibernateSession(
7156         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
7157         new HibernateHandler() {
7158 
7159           public Object callback(HibernateHandlerBean hibernateHandlerBean)
7160               throws GrouperDAOException {
7161 
7162             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
7163 
7164             if (checkSecurity) {
7165               // verify that the subject has read privileges to the group
7166               if (!PrivilegeHelper.canRead(GrouperSession.staticGrouperSession(), Group.this,
7167                   GrouperSession.staticGrouperSession().getSubject())) {
7168                 throw new InsufficientPrivilegeException(E.CANNOT_READ);
7169               }
7170               
7171               // verify access to group types
7172               for (AttributeAssign assign : Group.this.internal_getGroupTypeAssignments().values()) {
7173                 if (!PrivilegeHelper.canViewAttributeAssign(GrouperSession.staticGrouperSession(), assign, true)) {
7174                   throw new InsufficientPrivilegeException("Unable to read attribute assignment: " + assign.getId());
7175                 }
7176               }
7177             }
7178             
7179             GrouperSession actAs = null;
7180             if (addDefaultGroupPrivileges == true) {
7181               actAs = GrouperSession.staticGrouperSession();
7182             } else {
7183               actAs = GrouperSession.staticGrouperSession().internal_getRootSession();
7184             }
7185             
7186             Map<String, String> attributesMap = new HashMap<String, String>();
7187             if (attributes == true) {                  
7188               Map filtered = new HashMap();
7189               Map.Entry<String, Attribute> kv;
7190               Iterator<Map.Entry<String, Attribute>> it = Group.this.getAttributesMap(false).entrySet().iterator();
7191               while (it.hasNext()) {
7192                 kv = it.next();
7193 
7194                 if (checkSecurity) {
7195                   // check security
7196                   AttributeAssignFinder.findById(kv.getValue().getId(), true);
7197                 }
7198                 
7199                 filtered.put((String)kv.getKey(), (String) kv.getValue().getValue());
7200               }
7201               attributesMap = filtered;
7202             }
7203             
7204                 Group newGroup = null;
7205                 String theExtension = extension == null ? Group.this.getExtension() : extension;
7206                 String theDisplayExtension = displayExtension == null ? Group.this.getDisplayExtensionDb() : displayExtension;
7207                 
7208                 try {
7209                   newGroup = stem.internal_addChildGroup(theExtension,
7210                       theDisplayExtension, null, Group.this
7211                           .description, Group.this.getTypesDb(), attributesMap,
7212                       addDefaultGroupPrivileges, Group.this.typeOfGroup, checkSecurity);
7213                 } catch (GroupAddException e) {
7214                   Group test = GroupFinder.findByName(GrouperSession
7215                       .staticGrouperSession().internal_getRootSession(), stem.getName()
7216                       + Stem.DELIM + theExtension, false);
7217                   if (test == null) {
7218                     throw e;
7219                   }
7220                   
7221                   // if the group already exists in the new stem, lets append "_#" to the extension.
7222                   String newGroupExtension = theExtension + "_2";
7223                   int extensionCount = 2;
7224                   boolean notFound = false;
7225                   while (notFound == false) {
7226                     Group foundGroup = GroupFinder.findByName(GrouperSession
7227                         .staticGrouperSession().internal_getRootSession(), stem.getName()
7228                         + Stem.DELIM + newGroupExtension, false);
7229                     if (foundGroup != null) {
7230                       extensionCount++;
7231                       newGroupExtension = theExtension + "_"
7232                           + extensionCount;
7233                     } else {
7234                       notFound = true;
7235                     }
7236                   }
7237                   
7238                   newGroup = stem.internal_addChildGroup(newGroupExtension,
7239                       theDisplayExtension, null, Group.this
7240                           .description, Group.this.getTypesDb(), attributesMap,
7241                 addDefaultGroupPrivileges, Group.this.typeOfGroup, checkSecurity);
7242                 }
7243             
7244             if (composite) {
7245               Composite oldComposite = GrouperDAOFactory.getFactory()
7246                   .getComposite().findAsOwner(Group.this, false);
7247               if (oldComposite != null) {
7248                 String leftFactorUuid = oldComposite.getLeftFactorUuid();
7249                 String rightFactorUuid = oldComposite.getRightFactorUuid();
7250   
7251                 Group leftFactorGroup = GroupFinder
7252                     .findByUuid(GrouperSession.staticGrouperSession()
7253                         .internal_getRootSession(), leftFactorUuid, true);
7254                 Group rightFactorGroup = GroupFinder.findByUuid(GrouperSession
7255                     .staticGrouperSession().internal_getRootSession(),
7256                     rightFactorUuid, true);
7257   
7258                 newGroup.internal_addCompositeMember(actAs, oldComposite.getType(),
7259                     leftFactorGroup, rightFactorGroup, null);
7260               }
7261             }
7262 
7263             if (privilegesOfGroup == true) {
7264               newGroup.internal_copyPrivilegesOfGroup(actAs, Group.this);
7265             }
7266 
7267             if (groupAsPrivilege == true) {
7268               newGroup.internal_copyGroupAsPrivilege(GrouperSession.staticGrouperSession(), Group.this);
7269             }
7270 
7271             if (listMembersOfGroup == true) {
7272               newGroup.internal_copyListMembersOfGroup(actAs, Group.this);
7273             }
7274 
7275             if (listGroupAsMember == true) {
7276               newGroup.internal_copyGroupAsMember(GrouperSession.staticGrouperSession(), Group.this);
7277             }
7278             
7279             final Group NEW_GROUP = newGroup;
7280             
7281             GrouperSession.callbackGrouperSession(GrouperSession.staticGrouperSession().internal_getRootSession(), new GrouperSessionHandler() {
7282               
7283               /**
7284                * @see edu.internet2.middleware.grouper.misc.GrouperSessionHandler#callback(edu.internet2.middleware.grouper.GrouperSession)
7285                */
7286               public Object callback(GrouperSession rootSession) throws GrouperSessionException {
7287 
7288                 // may need to copy over the subject identifier if this is an entity
7289                 if (TypeOfGroup.entity == NEW_GROUP.getTypeOfGroup()) {
7290                   String oldPrefix = GrouperUtil.parentStemNameFromName(Group.this.getName()) + ":";
7291                   String newPrefix = GrouperUtil.parentStemNameFromName(NEW_GROUP.getName()) + ":";
7292                   
7293                   if (!oldPrefix.equals(newPrefix)) {
7294                     String subjectIdentifier = Group.this.getAttributeValueDelegate().retrieveValueString(EntityUtils.entitySubjectIdentifierName());
7295                     if (subjectIdentifier != null && subjectIdentifier.startsWith(oldPrefix)) {
7296                       subjectIdentifier = newPrefix + subjectIdentifier.substring(oldPrefix.length());
7297                       NEW_GROUP.getAttributeValueDelegate().assignValue(EntityUtils.entitySubjectIdentifierName(), subjectIdentifier);
7298                     }
7299                   }
7300                 }
7301                 
7302                 return null;
7303               }
7304             });
7305             
7306             //if not a smaller operation of a larger auditable call
7307             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
7308 
7309               AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_COPY,
7310                   "oldGroupId", Group.this.getUuid(), "oldGroupName", 
7311                   Group.this.getName(), "newGroupId", newGroup.getUuid(), "newGroupName",
7312                   newGroup.getName(), 
7313                   "privilegesOfGroup", privilegesOfGroup ? "T" : "F", "groupAsPrivilege",
7314                   groupAsPrivilege ? "T" : "F", "listMembersOfGroup",
7315                   listMembersOfGroup ? "T" : "F", "listGroupAsMember",
7316                   listGroupAsMember ? "T" : "F");
7317               auditEntry.setInt01(attributes ? 1L : 0L);
7318               auditEntry.setDescription("Copy group " + Group.this.getName() + " to name: " + newGroup.getName()
7319                   + ", privilegesOfGroup? " + (privilegesOfGroup ? "T" : "F")
7320                   + ", groupAsPrivilege? " + (groupAsPrivilege ? "T" : "F") 
7321                   + ", listMembersOfGroup? " + (listMembersOfGroup ? "T" : "F") 
7322                   + ", listGroupAsMember? " + (listGroupAsMember ? "T" : "F") 
7323                   + ", attributes? " + (attributes ? "T" : "F")); 
7324               auditEntry.saveOrUpdate(true);
7325             }
7326             
7327             return newGroup;
7328           }
7329         });
7330   }
7331 
7332   /**
7333    * 
7334    * @param session
7335    * @param group
7336    * @throws SchemaException
7337    * @throws MemberAddException
7338    * @throws GrouperException
7339    * @throws InsufficientPrivilegeException
7340    */
7341   private void internal_copyGroupAsMember(GrouperSession session, Group group)
7342       throws SchemaException, MemberAddException, GrouperException,
7343       InsufficientPrivilegeException {
7344 
7345     Set<Membership> memberships = GrouperDAOFactory.getFactory().getMembership()
7346         .findAllImmediateByMemberAndFieldType(group.toMember().getUuid(),
7347             FieldType.LIST.toString(), false);
7348     
7349     if (memberships.size() == 0) {
7350       return;
7351     }
7352     
7353     Member member = MemberFinder.findBySubject(session, this.toSubject(), true);
7354 
7355     Iterator<Membership> membershipsIter = memberships.iterator();
7356     while (membershipsIter.hasNext()) {
7357       Membership existingMembership = membershipsIter.next();
7358       Field f = FieldFinder.findById(existingMembership.getFieldId(), true);
7359       Group g = existingMembership.getOwnerGroup();
7360       PrivilegeHelper.dispatch(session, g, session.getSubject(), f.getWritePriv());
7361       Membership copiedMembership = existingMembership.clone();
7362       copiedMembership.setMemberUuid(member.getUuid());
7363       copiedMembership.setMember(member);
7364       copiedMembership.setCreatorUuid(session.getMemberUuid());
7365       copiedMembership.setCreateTimeLong(new Date().getTime());
7366       copiedMembership.setImmediateMembershipId(GrouperUuid.getUuid());
7367       copiedMembership.setHibernateVersionNumber(-1L);
7368       GrouperDAOFactory.getFactory().getMembership().save(copiedMembership);
7369     }
7370   }
7371 
7372   /**
7373    * 
7374    * @param session
7375    * @param group
7376    * @throws SchemaException
7377    * @throws MemberAddException
7378    * @throws InsufficientPrivilegeException
7379    */
7380   private void internal_copyListMembersOfGroup(GrouperSession session, Group group)
7381       throws SchemaException, MemberAddException, InsufficientPrivilegeException {
7382     Set<Field> fields = FieldFinder.findAllByType(FieldType.LIST);
7383     Iterator<Field> iter = fields.iterator();
7384     while (iter.hasNext()) {
7385       Field f = iter.next();
7386       
7387       if (f.getUuid().equals(Group.getDefaultList().getUuid()) || group.hasType(f.getGroupType(true))) {
7388         PrivilegeHelper.dispatch(session, group, session.getSubject(), f.getReadPriv());
7389         Iterator<Membership> membershipsIter = GrouperDAOFactory.getFactory().getMembership()
7390             .findAllByGroupOwnerAndFieldAndType(group.getUuid(), f,
7391                 MembershipType.IMMEDIATE.getTypeString(), false).iterator();
7392 
7393         while (membershipsIter.hasNext()) {
7394           Membership existingMembership = membershipsIter.next();
7395           Membership copiedMembership = existingMembership.clone();
7396           copiedMembership.setOwnerGroupId(this.getUuid());
7397           copiedMembership.setCreatorUuid(session.getMemberUuid());
7398           copiedMembership.setCreateTimeLong(new Date().getTime());
7399           copiedMembership.setImmediateMembershipId(GrouperUuid.getUuid());
7400           copiedMembership.setHibernateVersionNumber(-1L);
7401           GrouperDAOFactory.getFactory().getMembership().save(copiedMembership);
7402         }
7403       }
7404     }
7405   }
7406 
7407   /**
7408    * 
7409    * @param session
7410    * @param group
7411    * @throws UnableToPerformException
7412    */
7413   private void internal_copyGroupAsPrivilege(GrouperSession session, Group group) 
7414     throws UnableToPerformException {
7415     Set<Privilege> privileges = Privilege.getAccessPrivs();
7416 
7417     Iterator<Privilege> iter = privileges.iterator();
7418     while (iter.hasNext()) {
7419       Privilege priv = iter.next();
7420       session.getAccessResolver().privilegeCopy(group.toSubject(), this.toSubject(), priv);
7421     }
7422     
7423     privileges = Privilege.getNamingPrivs();
7424     iter = privileges.iterator();
7425     while (iter.hasNext()) {
7426       Privilege priv = iter.next();
7427       session.getNamingResolver().privilegeCopy(group.toSubject(), this.toSubject(), priv);
7428     } 
7429   }
7430 
7431   /**
7432    * 
7433    * @param session
7434    * @param group
7435    * @throws UnableToPerformException
7436    */
7437   private void internal_copyPrivilegesOfGroup(GrouperSession session, Group group)
7438     throws UnableToPerformException {
7439     Set<Privilege> privileges = Privilege.getAccessPrivs();
7440 
7441     Iterator<Privilege> iter = privileges.iterator();
7442     while (iter.hasNext()) {
7443       Privilege priv = iter.next();
7444       session.getAccessResolver().privilegeCopy(group, this, priv);      
7445     }  
7446   }
7447 
7448   /**
7449    * Copy this group to another Stem.  If you want to specify options
7450    * for the copy, use GroupCopy.  This will use the default options.
7451    * @param stem
7452    * @return the new group
7453    * @throws InsufficientPrivilegeException 
7454    * @throws GroupAddException 
7455    */
7456   public Group copy(Stem stem) throws GroupAddException, InsufficientPrivilegeException {
7457     GroupCopyroupCopy.html#GroupCopy">GroupCopy groupCopy = new GroupCopy(this, stem);
7458     return groupCopy.save();
7459   }
7460 
7461   /**
7462    * type of group, group or role 
7463    * @return group or role
7464    */
7465   public TypeOfGroup getTypeOfGroup() {
7466     return this.typeOfGroup;
7467   }
7468 
7469   /**
7470    * type of group, group or role
7471    * @param typeOfGroup1
7472    */
7473   public void setTypeOfGroup(TypeOfGroup typeOfGroup1) {
7474     this.typeOfGroup = typeOfGroup1;
7475   } 
7476 
7477   /**
7478    * type of group, group or role 
7479    * @return group or role
7480    */
7481   public String getTypeOfGroupDb() {
7482     return this.typeOfGroup == null ? TypeOfGroup.group.name() : this.typeOfGroup.name();
7483   }
7484 
7485   /**
7486    * type of group, group or role
7487    * @param typeOfGroup1
7488    */
7489   public void setTypeOfGroupDb(String typeOfGroup1) {
7490     this.typeOfGroup = TypeOfGroup.valueOfIgnoreCase(typeOfGroup1, false);
7491   }
7492 
7493   /**
7494    * @see edu.internet2.middleware.grouper.grouperSet.GrouperSetElement#__getId()
7495    */
7496   public String __getId() {
7497     return this.getUuid();
7498   }
7499 
7500   /**
7501    * @see edu.internet2.middleware.grouper.grouperSet.GrouperSetElement#__getName()
7502    */
7503   public String __getName() {
7504     return this.getName();
7505   }
7506 
7507   /**
7508    * @see edu.internet2.middleware.grouper.permissions.role.Role#getId()
7509    */
7510   public String getId() {
7511     return this.getUuid();
7512   }
7513 
7514   /**
7515    * id of the group as a unique integer
7516    * @return id
7517    */
7518   public Long getIdIndex() {
7519     return this.idIndex;
7520   }
7521 
7522   /**
7523    * id of the group as a unique integer
7524    * @param idIndex1
7525    */
7526   public void setIdIndex(Long idIndex1) {
7527     this.idIndex = idIndex1;
7528   }
7529 
7530   /**
7531    * @see edu.internet2.middleware.grouper.permissions.role.Role#getStemId()
7532    */
7533   public String getStemId() {
7534     return this.getParentUuid();
7535   }
7536 
7537   /**
7538    * @see edu.internet2.middleware.grouper.permissions.role.Role#setId(java.lang.String)
7539    */
7540   public void setId(String id1) {
7541     this.setUuid(id1);
7542   }
7543 
7544   /**
7545    * @see edu.internet2.middleware.grouper.permissions.role.Role#setStemId(java.lang.String)
7546    */
7547   public void setStemId(String stemId1) {
7548     this.setParentUuid(stemId1);
7549   }
7550 
7551   /**
7552    * cache this for performance.  delegate calls to this class for role hierarchy stuff
7553    */
7554   @GrouperIgnoreClone @GrouperIgnoreDbVersion @GrouperIgnoreFieldConstant
7555   private RoleInheritanceDelegate roleInheritanceDelegate = null;
7556   
7557   /**
7558    * delegate calls to this class for role hierarchy stuff
7559    * @return the role inheritance hierarchy
7560    */
7561   public RoleInheritanceDelegate getRoleInheritanceDelegate() {
7562     if (this.roleInheritanceDelegate == null) {
7563       this.roleInheritanceDelegate = new RoleInheritanceDelegate(this);
7564     }
7565     return this.roleInheritanceDelegate;
7566   }
7567   
7568   /**
7569    * cache this for performance.  delegate calls to this class for role hierarchy stuff
7570    */
7571   @GrouperIgnoreClone @GrouperIgnoreDbVersion @GrouperIgnoreFieldConstant
7572   private PermissionRoleDelegate permissionRoleDelegate = null;
7573 
7574   /**
7575    * @see edu.internet2.middleware.grouper.permissions.role.Role#getPermissionRoleDelegate()
7576    */
7577   public PermissionRoleDelegate getPermissionRoleDelegate() {
7578     if (this.permissionRoleDelegate == null) {
7579       this.permissionRoleDelegate = new PermissionRoleDelegate(this);
7580     }
7581     return this.permissionRoleDelegate;
7582   }
7583 
7584   /**
7585    * Get memberships of this group, for a certain collection of members, must be enabled
7586    * 
7587    * A membership is the object which represents a join of member
7588    * and group.  Has metadata like type and creator,
7589    * and, if an effective membership, the parent membership
7590    * 
7591    * <pre class="eg">
7592    * Set memberships = g.getMemberships(f);
7593    * </pre>
7594    * @param   f Get memberships in this list field.
7595    * @param members 
7596    * @return  A set of {@link Membership} objects.
7597    * @throws  SchemaException
7598    */
7599   public Set<Membership> getNonImmediateMemberships(Field f, Collection<Member> members) 
7600     throws  SchemaException {
7601     return getNonImmediateMemberships(f, members, true);
7602   }
7603 
7604   /**
7605    * Get memberships of this group, for a certain collection of members
7606    * 
7607    * A membership is the object which represents a join of member
7608    * and group.  Has metadata like type and creator,
7609    * and, if an effective membership, the parent membership
7610    * 
7611    * <pre class="eg">
7612    * Set memberships = g.getMemberships(f);
7613    * </pre>
7614    * @param   f Get memberships in this list field.
7615    * @param members 
7616    * @param enabledOnly
7617    * @return  A set of {@link Membership} objects.
7618    * @throws  SchemaException
7619    */
7620   public Set<Membership> getNonImmediateMemberships(Field f, Collection<Member> members, boolean enabledOnly) 
7621     throws  SchemaException {
7622     return PrivilegeHelper.canViewMemberships( 
7623         GrouperSession.staticGrouperSession(), GrouperDAOFactory.getFactory().getMembership()
7624           .findAllByGroupOwnerAndFieldAndMembersAndType(this.getUuid(), f, members, 
7625               MembershipType.NONIMMEDIATE.getTypeString(), enabledOnly)
7626       );
7627   }
7628 
7629   /**
7630    * Get non immediate members of this group.  
7631    * 
7632    * An immediate member is directly assigned to a group.
7633    * A composite group has no immediate members.  Note that a 
7634    * member can have 0 to 1 immediate memberships
7635    * to a single group, and 0 to many effective memberships to a group.
7636    * A group can have potentially unlimited effective 
7637    * memberships
7638    * 
7639    * <pre class="eg">
7640    * Set nonImmediates = g.getNonImmediateMembers();
7641    * </pre>
7642    * @return  A set of {@link Member} objects.
7643    * @throws  GrouperException
7644    */
7645   public Set<Member> getNonImmediateMembers() 
7646     throws  GrouperException
7647   {
7648     try {
7649       return this.getNonImmediateMembers(getDefaultList());
7650     }
7651     catch (SchemaException eS) {
7652       // If we don't have "members" we have serious issues
7653       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
7654       LOG.fatal(msg);
7655       throw new GrouperException(msg, eS);
7656     }
7657   } // public Set getImmediateMembers()
7658 
7659   /**
7660    * Get non-immediate members of this group.  
7661    * 
7662    * An immediate member is directly assigned to a group.
7663    * A composite group has no immediate members.  Note that a 
7664    * member can have 0 to 1 immediate memberships
7665    * to a single group, and 0 to many effective memberships to a group.
7666    * A group can have potentially unlimited effective 
7667    * memberships
7668    * 
7669    * <pre class="eg">
7670    * Set nonImmediates = g.getNonImmediateMembers(f);
7671    * </pre>
7672    * @param   f Get members in this list field.
7673    * @return  A set of {@link Member} objects.
7674    * @throws  SchemaException
7675    */
7676   public Set<Member> getNonImmediateMembers(Field f) 
7677     throws  SchemaException {
7678     return getNonImmediateMembers(f, null);
7679   }
7680 
7681   /**
7682    * Get non-immediate members of this group.  
7683    * 
7684    * An immediate member is directly assigned to a group.
7685    * A composite group has no immediate members.  Note that a 
7686    * member can have 0 to 1 immediate memberships
7687    * to a single group, and 0 to many effective memberships to a group.
7688    * A group can have potentially unlimited effective 
7689    * memberships
7690    * 
7691    * <pre class="eg">
7692    * Set nonImmediates = g.getNonImmediateMembers(f);
7693    * </pre>
7694    * @param   f Get members in this list field.
7695    * @param queryOptions 
7696    * @return  A set of {@link Member} objects.
7697    * @throws  SchemaException
7698    */
7699   public Set<Member> getNonImmediateMembers(Field f, QueryOptions queryOptions) 
7700     throws  SchemaException {
7701     return getNonImmediateMembers(f, null, queryOptions);
7702   } // public Set getImmediateMembers(f)
7703 
7704   /**
7705    * Get non-immediate members of this group.  
7706    * 
7707    * An immediate member is directly assigned to a group.
7708    * A composite group has no immediate members.  Note that a 
7709    * member can have 0 to 1 immediate memberships
7710    * to a single group, and 0 to many effective memberships to a group.
7711    * A group can have potentially unlimited effective 
7712    * memberships
7713    * 
7714    * <pre class="eg">
7715    * Set nonImmediates = g.getNonImmediateMembers(f);
7716    * </pre>
7717    * @param   f Get members in this list field.
7718    * @param sources 
7719    * @param queryOptions 
7720    * @return  A set of {@link Member} objects.
7721    * @throws  SchemaException
7722    */
7723   public Set<Member> getNonImmediateMembers(Field f, Set<Source> sources, QueryOptions queryOptions) 
7724     throws  SchemaException {
7725     return MemberFinder.internal_findMembersByType(
7726       GrouperSession.staticGrouperSession(), this, f, MembershipType.NONIMMEDIATE.getTypeString(), sources, queryOptions,
7727       null, null, null
7728     );
7729   } // public Set getImmediateMembers(f)
7730 
7731   /**
7732    * Get nonimmediate memberships of this group.  
7733    * 
7734    * An immediate member is directly assigned to a group.
7735    * A composite group has no immediate members.  Note that a 
7736    * member can have 0 to 1 immediate memberships
7737    * to a single group, and 0 to many effective memberships to a group.
7738    * A group can have potentially unlimited effective 
7739    * memberships
7740    * 
7741    * A membership is the object which represents a join of member
7742    * and group.  Has metadata like type and creator,
7743    * and, if an effective membership, the parent membership
7744    * 
7745    * <pre class="eg">
7746    * Set immediates = g.getImmediateMemberships();
7747    * </pre>
7748    * @return  A set of {@link Membership} objects.
7749    * @throws  GrouperException
7750    */
7751   public Set<Membership> getNonImmediateMemberships() 
7752     throws  GrouperException
7753   {
7754     try {
7755       return this.getNonImmediateMemberships(getDefaultList());
7756     }
7757     catch (SchemaException eS) {
7758       // If we don't have "members" we have serious issues
7759       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
7760       LOG.fatal(msg);
7761       throw new GrouperException(msg, eS);
7762     }
7763   } // public Set getImmediateMemberships()
7764 
7765   /**
7766    * An immediate member is directly assigned to a group.
7767    * A composite group has no immediate members.  Note that a 
7768    * member can have 0 to 1 immediate memberships
7769    * to a single group, and 0 to many effective memberships to a group.
7770    * A group can have potentially unlimited effective 
7771    * memberships
7772    * 
7773    * A membership is the object which represents a join of member
7774    * and group.  Has metadata like type and creator,
7775    * and, if an effective membership, the parent membership
7776    * 
7777    * <pre class="eg">
7778    * Set immediates = g.getNonImmediateMemberships(f);
7779    * </pre>
7780    * @param   f Get memberships in this list field.
7781    * @return  A set of {@link Membership} objects.
7782    * @throws  SchemaException
7783    */
7784   public Set<Membership> getNonImmediateMemberships(Field f) 
7785     throws  SchemaException
7786   {
7787     GrouperSession.validate(GrouperSession.staticGrouperSession());
7788     return MembershipFinder.internal_findAllByGroupOwnerAndFieldAndType(
7789       GrouperSession.staticGrouperSession(), this, f, MembershipType.NONIMMEDIATE.getTypeString()
7790     );
7791   } // public Set getImmediateMemberships(f)
7792 
7793   /**
7794    * Check whether the subject is a non immediate member of this group.  
7795    * 
7796    * An immediate member is directly assigned to a group.
7797    * A composite group has no immediate members.  Note that a 
7798    * member can have 0 to 1 immediate memberships
7799    * to a single group, and 0 to many effective memberships to a group.
7800    * A group can have potentially unlimited effective 
7801    * memberships
7802    * 
7803    * <pre class="eg">
7804    * if (g.hasImmediateMember(subj)) {
7805    *   // Subject is an immediate member of this group
7806    * }
7807    * else {
7808    *   // Subject is not a immediate member of this group
7809    * } 
7810    * </pre>
7811    * @param   subj  Check this subject.
7812    * @return  Boolean true if subject belongs to this group.
7813    * @throws  GrouperException
7814    */
7815   public boolean hasNonImmediateMember(Subject subj) 
7816     throws  GrouperException
7817   {
7818     try {
7819       return this.hasNonImmediateMember(subj, getDefaultList());
7820     }
7821     catch (SchemaException eS) {
7822       // If we don't have "members" we have serious issues
7823       String msg = E.GROUP_NODEFAULTLIST + eS.getMessage();
7824       LOG.fatal(msg);
7825       throw new GrouperException(msg, eS);
7826     }
7827   } 
7828 
7829   /**
7830    * Check whether the subject is a non immediate member of this group.
7831    * 
7832    * An immediate member is directly assigned to a group.
7833    * A composite group has no immediate members.  Note that a 
7834    * member can have 0 to 1 immediate memberships
7835    * to a single group, and 0 to many effective memberships to a group.
7836    * A group can have potentially unlimited effective 
7837    * memberships
7838    * 
7839    * <pre class="eg">
7840    * if (g.hasImmediateMember(subj, f)) {
7841    *   // Subject is an immediate member of this group
7842    * }
7843    * else {
7844    *   // Subject is not a immediate member of this group
7845    * } 
7846    * </pre>
7847    * @param   subj  Check this subject.
7848    * @param   f     Check for membership in this list field.
7849    * @return  Boolean true if subject belongs to this group.
7850    * @throws  SchemaException
7851    */
7852   public boolean hasNonImmediateMember(Subject subj, Field f) 
7853     throws  SchemaException
7854   {
7855     boolean rv = false;
7856     Member m = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subj, true);
7857     rv = m.isNonImmediateMember(this, f);
7858     return rv;
7859   } // public boolean hasImmediateMember(subj, f)
7860 
7861   /**
7862    * Get {@link Composite} {@link Member}s of this group.
7863    * 
7864    * A composite group is composed of two groups and a set operator 
7865    * (stored in grouper_composites table)
7866    * (e.g. union, intersection, etc).  A composite group has no immediate members.
7867    * All subjects in a composite group are effective members.
7868    * 
7869    * <pre class="eg">
7870    * Set members = g.getCompositeMembers();
7871    * </pre>
7872    * @param field
7873    * @param sources 
7874    * @param queryOptions 
7875    * @return  A set of {@link Member} objects.
7876    * @since   1.0
7877    */
7878   public Set<Member> getCompositeMembers(Field field, Set<Source> sources, QueryOptions queryOptions) {
7879 
7880     return MemberFinder.internal_findMembersByType(
7881       GrouperSession.staticGrouperSession(), this, field, 
7882       MembershipType.COMPOSITE.getTypeString(), sources, queryOptions, null, null, null
7883     );
7884   } // public Set getCompositeMembers()
7885 
7886   /**
7887    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlCopyBusinessPropertiesToExisting(java.lang.Object)
7888    */
7889   public void xmlCopyBusinessPropertiesToExisting(Group existingRecord) {
7890     existingRecord.setAlternateNameDb(this.alternateNameDb);
7891     existingRecord.setDescriptionDb(this.getDescriptionDb());
7892     existingRecord.setDisplayExtensionDb(this.getDisplayExtensionDb());
7893     existingRecord.setDisplayNameDb(this.getDisplayNameDb());
7894     existingRecord.setExtensionDb(this.getExtensionDb());
7895     existingRecord.setNameDb(this.getNameDb());
7896     existingRecord.setParentUuid(this.getParentUuid());
7897     existingRecord.setTypeOfGroup(this.typeOfGroup);
7898     existingRecord.setUuid(this.getUuid());
7899     existingRecord.setDisabledTimeDb(this.getDisabledTimeDb());
7900     existingRecord.setEnabledDb(this.getEnabledDb());
7901     existingRecord.setEnabledTimeDb(this.getEnabledTimeDb());
7902   }
7903 
7904   /**
7905    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlDifferentBusinessProperties(java.lang.Object)
7906    */
7907   public boolean xmlDifferentBusinessProperties(Group other) {
7908     if (!GrouperUtil.equals(this.disabledTimeDb, other.disabledTimeDb)) {
7909       return true;
7910     }
7911     if (!GrouperUtil.equals(this.enabledTimeDb, other.enabledTimeDb)) {
7912       return true;
7913     }
7914     if (this.enabled != other.enabled) {
7915       return true;
7916     }
7917     if (!StringUtils.equals(this.alternateNameDb, other.alternateNameDb)) {
7918       return true;
7919     }
7920     if (!StringUtils.equals(StringUtils.trimToNull(this.description), StringUtils.trimToNull(other.description))) {
7921       return true;
7922     }
7923     if (!StringUtils.equals(this.displayExtension, other.displayExtension)) {
7924       return true;
7925     }
7926     if (!StringUtils.equals(this.displayName, other.displayName)) {
7927       return true;
7928     }
7929     if (!StringUtils.equals(this.extension, other.extension)) {
7930       return true;
7931     }
7932     if (!StringUtils.equals(this.name, other.name)) {
7933       return true;
7934     }
7935     if (!StringUtils.equals(this.parentUuid, other.parentUuid)) {
7936       return true;
7937     }
7938     if (!GrouperUtil.equals(this.typeOfGroup, other.typeOfGroup)) {
7939       return true;
7940     }
7941     if (!StringUtils.equals(this.uuid, other.uuid)) {
7942       return true;
7943     }
7944 
7945     return false;
7946   }
7947 
7948   /**
7949    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlDifferentUpdateProperties(java.lang.Object)
7950    */
7951   public boolean xmlDifferentUpdateProperties(Group other) {
7952     if (!StringUtils.equals(this.contextId, other.contextId)) {
7953       return true;
7954     }
7955     if (this.createTime != other.createTime) {
7956       return true;
7957     }
7958     if (!StringUtils.equals(this.creatorUUID, other.creatorUUID)) {
7959       return true;
7960     }
7961     if (!GrouperUtil.equals(this.getHibernateVersionNumber(), other.getHibernateVersionNumber())) {
7962       return true;
7963     }
7964     //dont check this since after the import it changes anyways perhaps, so just leave it
7965 //    if (!GrouperUtil.equals(this.lastMembershipChangeDb, other.lastMembershipChangeDb)) {
7966 //      return true;
7967 //    }
7968     if (!StringUtils.equals(this.modifierUUID, other.modifierUUID)) {
7969       return true;
7970     }
7971     if (this.modifyTime != other.modifyTime) {
7972       return true;
7973     }
7974     return false;
7975   }
7976 
7977   /**
7978    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlRetrieveByIdOrKey()
7979    */
7980   public Group xmlRetrieveByIdOrKey() {
7981     return GrouperDAOFactory.getFactory().getGroup().findByUuidOrName(this.uuid, this.name, false,
7982         new QueryOptions().secondLevelCache(false));
7983   }
7984 
7985   /**
7986    * assign different id index
7987    * @param theIdIndex
7988    * @return if it was changed
7989    */
7990   public boolean assignIdIndex(final long theIdIndex) {
7991     
7992     TableIndex.assertCanAssignIdIndex();
7993 
7994     boolean needsSave = false;
7995 
7996     synchronized (TableIndexType.group) {
7997   
7998       //ok, if the index is not in use (not, it could be reserved... hmmm)
7999       Group tempGroup = GrouperDAOFactory.getFactory().getGroup().findByIdIndex(theIdIndex, false);
8000       if (tempGroup == null) {
8001         
8002         this.setIdIndex(theIdIndex);
8003         TableIndex.clearReservedId(TableIndexType.group, theIdIndex);
8004         needsSave = true;
8005         
8006         //do a new session so we don hold on too long
8007         HibernateSession.callbackHibernateSession(GrouperTransactionType.READ_WRITE_NEW, AuditControl.WILL_NOT_AUDIT, new HibernateHandler() {
8008           
8009           @Override
8010           public Object callback(HibernateHandlerBean hibernateHandlerBean)
8011               throws GrouperDAOException {
8012             //now we might need to increment the index
8013             TableIndex tableIndex = GrouperDAOFactory.getFactory().getTableIndex().findByType(TableIndexType.group);
8014             if (tableIndex != null && tableIndex.getLastIndexReserved() < theIdIndex) {
8015               tableIndex.setLastIndexReserved(theIdIndex);
8016               tableIndex.saveOrUpdate();
8017             }
8018             return null;
8019           }
8020         });
8021       }
8022       
8023       
8024     }
8025     return needsSave;
8026   }
8027   
8028   /**
8029    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlSaveBusinessProperties(java.lang.Object)
8030    */
8031   public Group/internet2/middleware/grouper/Group.html#Group">Group xmlSaveBusinessProperties(Group existingRecord) {
8032     //if its an insert, call the business method
8033     if (existingRecord == null) {
8034       Stem parent = this.getParentStem();
8035       if (this.getTypeOfGroup() == null || this.getTypeOfGroup().equals(TypeOfGroup.group)) {
8036         existingRecord = parent.internal_addChildGroup(
8037             this.extension, this.displayExtension, this.uuid, this.description, null, null, false, this.typeOfGroup, true);
8038       } else if (this.getTypeOfGroup().equals(TypeOfGroup.role)) {
8039         existingRecord = (Group)parent.internal_addChildRole(this.extension, this.displayExtension, this.uuid);
8040       } else {
8041         throw new RuntimeException("Not expecting type of group: " + this.getTypeOfGroup());
8042       }
8043       if (this.idIndex != null) {
8044         existingRecord.assignIdIndex(this.idIndex);
8045       }
8046     }
8047     this.xmlCopyBusinessPropertiesToExisting(existingRecord);
8048     //if its an insert or update, then do the rest of the fields
8049     existingRecord.store();
8050     return existingRecord;
8051   }
8052 
8053   /**
8054    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlSaveUpdateProperties()
8055    */
8056   public void xmlSaveUpdateProperties() {
8057     GrouperDAOFactory.getFactory().getGroup().saveUpdateProperties(this);
8058   }
8059 
8060   /**
8061    * convert to xml bean for export
8062    * @param grouperVersion
8063    * @return xml bean
8064    */
8065   public XmlExportGroup xmlToExportGroup(GrouperVersion grouperVersion) {
8066     if (grouperVersion == null) {
8067       throw new RuntimeException();
8068     }
8069     
8070     XmlExportGroupXmlExportGroup.html#XmlExportGroup">XmlExportGroup xmlExportGroup = new XmlExportGroup();
8071     xmlExportGroup.setAlternateName(this.getAlternateNameDb());
8072     xmlExportGroup.setContextId(this.getContextId());
8073     xmlExportGroup.setCreateTime(GrouperUtil.dateStringValue(this.getCreateTime()));
8074     xmlExportGroup.setCreatorId(this.getCreatorUuid());
8075     xmlExportGroup.setDescription(this.getDescription());
8076     xmlExportGroup.setDisplayExtension(this.getDisplayExtension());
8077     xmlExportGroup.setDisplayName(this.getDisplayName());
8078     xmlExportGroup.setExtension(this.getExtension());
8079     xmlExportGroup.setHibernateVersionNumber(this.getHibernateVersionNumber());
8080     xmlExportGroup.setIdIndex(this.getIdIndex());
8081     //TODO make string
8082     xmlExportGroup.setModifierId(this.getModifierUuid());
8083     xmlExportGroup.setModifierTime(GrouperUtil.dateStringValue(this.getModifyTime()));
8084     xmlExportGroup.setName(this.getName());
8085     xmlExportGroup.setParentStem(this.getParentUuid());
8086     xmlExportGroup.setTypeOfGroup(this.getTypeOfGroupDb());
8087     xmlExportGroup.setUuid(this.getUuid());
8088     
8089     xmlExportGroup.setDisableTimestamp(GrouperUtil.dateStringValue(this.getDisabledTimeDb()));
8090     xmlExportGroup.setEnabled(GrouperUtil.booleanValue(this.getEnabledDb(), true) ? "T" : "F");
8091     xmlExportGroup.setEnabledTimestamp(GrouperUtil.dateStringValue(this.getEnabledTimeDb()));
8092      
8093     return xmlExportGroup;
8094   }
8095 
8096   /**
8097    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlGetId()
8098    */
8099   public String xmlGetId() {
8100     return this.getId();
8101   }
8102 
8103   /**
8104    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlSetId(java.lang.String)
8105    */
8106   public void xmlSetId(String theId) {
8107     this.setId(theId);
8108   }
8109 
8110   /**
8111    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlToString()
8112    */
8113   public String xmlToString() {
8114     return "Group: " + this.uuid + ", " + this.name;
8115   }
8116 
8117   /**
8118    * @see GrouperObject#matchesLowerSearchStrings(Set)
8119    */
8120   @Override
8121   public boolean matchesLowerSearchStrings(Set<String> filterStrings) {
8122 
8123     if (GrouperUtil.length(filterStrings) == 0) {
8124       return true;
8125     }
8126 
8127     String lowerId = this.getId().toLowerCase();
8128     String lowerName = StringUtils.defaultString(this.getName()).toLowerCase();
8129     String lowerDisplayName = StringUtils.defaultString(this.getDisplayName()).toLowerCase();
8130     String lowerDescription = StringUtils.defaultString(this.getDescription()).toLowerCase();
8131     String lowerAlternateName = StringUtils.defaultString(this.getAlternateName()).toLowerCase();
8132     
8133     for (String filterString : GrouperUtil.nonNull(filterStrings)) {
8134       
8135       //if all dont match, return false
8136       if (!lowerId.contains(filterString)
8137           && !lowerName.contains(filterString)
8138           && !lowerDisplayName.contains(filterString)
8139           && !lowerDescription.contains(filterString)
8140           && !lowerAlternateName.contains(filterString)) {
8141         return false;
8142       }
8143       
8144     }
8145     return true;
8146   }
8147 
8148   /**
8149    * see if the subject has a privilege or another privilege that implies this privilege.
8150    * @param subject
8151    * @param privilegeOrListName
8152    * @param secure if the user must be an admin to check
8153    * @return true if has privilege
8154    */
8155   public boolean canHavePrivilege(Subject subject, String privilegeOrListName, boolean secure) {
8156     
8157     GrouperSession grouperSession = GrouperSession.staticGrouperSession();
8158     
8159     if (secure) {
8160       PrivilegeHelper.dispatch(grouperSession, this, grouperSession.getSubject(), AccessPrivilege.ADMIN);
8161     }
8162     
8163     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.ADMIN.getName()) 
8164         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.ADMIN.getListName())) {
8165       return PrivilegeHelper.canAdmin(grouperSession, this, subject);
8166     }
8167     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.READ.getName()) 
8168         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.READ.getListName())) {
8169       return PrivilegeHelper.canRead(grouperSession, this, subject);
8170     }
8171     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.VIEW.getName()) 
8172         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.VIEW.getListName())) {
8173       return PrivilegeHelper.canView(grouperSession, this, subject);
8174     }
8175     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.UPDATE.getName()) 
8176         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.UPDATE.getListName())) {
8177       return PrivilegeHelper.canUpdate(grouperSession, this, subject);
8178     }
8179     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.GROUP_ATTR_READ.getName()) 
8180         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.GROUP_ATTR_READ.getListName())) {
8181       return PrivilegeHelper.canGroupAttrRead(grouperSession, this, subject);
8182     }
8183     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.GROUP_ATTR_UPDATE.getName()) 
8184         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.GROUP_ATTR_UPDATE.getListName())) {
8185       return PrivilegeHelper.canGroupAttrUpdate(grouperSession, this, subject);
8186     }
8187     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.OPTIN.getName()) 
8188         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.OPTIN.getListName())) {
8189       return PrivilegeHelper.canOptin(grouperSession, this, subject);
8190     }
8191     if (StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.OPTOUT.getName()) 
8192         || StringUtils.equalsIgnoreCase(privilegeOrListName, AccessPrivilege.OPTOUT.getListName())) {
8193       return PrivilegeHelper.canOptout(grouperSession, this, subject);
8194     }
8195     throw new RuntimeException("Cant find privilege: '" + privilegeOrListName + "'");
8196   
8197   }
8198   
8199   /**
8200    * if group is in include/exclude etc change other groups
8201    */
8202   private void changeDisplayProperties() {
8203     
8204     boolean alreadyChanged = false;
8205     
8206     String includeExcludeTypeName = GrouperConfig.retrieveConfig().propertyValueString("grouperIncludeExclude.type.name");
8207     String requireGroupsTypeName = GrouperConfig.retrieveConfig().propertyValueString("grouperIncludeExclude.requireGroups.type.name");
8208     
8209     // group before the changes
8210     Group groupBeforeChange = GroupFinder.findByUuid(GrouperSession.staticGrouperSession(), this.getUuid(), false, 
8211         new QueryOptions().secondLevelCache(false));
8212     DisplayProperties oldDisplayProperties = new DisplayProperties(groupBeforeChange);
8213     DisplayProperties newDisplayProperties = new DisplayProperties(this);
8214     
8215     for (GroupType groupType: this.getTypes(false)) {
8216      
8217       if (groupType.getName().equals(includeExcludeTypeName)) {
8218         changeDisplayPropertiesForIncludeExcludeType(oldDisplayProperties, newDisplayProperties);
8219         alreadyChanged = true;
8220       } else if (groupType.getName().equals(requireGroupsTypeName)) {
8221         changeDisplayPropertiesForRequireInGroupsType(oldDisplayProperties, newDisplayProperties);
8222         alreadyChanged = true;
8223       }
8224       
8225     }
8226     
8227     // addIncludeExclude type from UI - systemOfRecord group is not of type addIncludeExclude
8228     if (!alreadyChanged && groupBeforeChange.getName().endsWith(systemOfRecordExtensionSuffix()) 
8229           && groupExistsAsCompositeInOverallGroup(groupBeforeChange.getName())) {
8230         changeDisplayPropertiesForIncludeExcludeType(oldDisplayProperties, newDisplayProperties);
8231         alreadyChanged = true;
8232     }
8233     
8234     // requireInGroups type from UI - systemOfRecord group is not of type requireInGroups
8235     if (!alreadyChanged && groupBeforeChange.getName().endsWith(systemOfRecordExtensionSuffix()) 
8236           && groupExistsAsMemberInOverallGroup(groupBeforeChange.getName())) {
8237       changeDisplayPropertiesForRequireInGroupsType(oldDisplayProperties, newDisplayProperties);
8238     }
8239     
8240   }
8241   
8242   /**
8243    * 
8244    * @param groupName
8245    * @return if group exists as direct member
8246    */
8247   private boolean groupExistsAsMemberInOverallGroup(String groupName) {
8248     
8249     // find overall group and it must not be null
8250     if (!groupName.endsWith(systemOfRecordExtensionSuffix())) {
8251       return false;
8252     }
8253     String overallGroupName = groupName.substring(0, groupName.length() - systemOfRecordExtensionSuffix().length());
8254     Group overallGroup = GroupFinder.findByName(GrouperSession.staticGrouperSession(), overallGroupName, false, 
8255         new QueryOptions().secondLevelCache(false));
8256     
8257     boolean found = false;
8258     if (overallGroup != null) {
8259       
8260       return overallGroup.hasImmediateMember(this.toSubject());
8261     }
8262     
8263     return found;
8264   }
8265   
8266   /**
8267    * @param groupName
8268    * @return true if group is composite in overall
8269    */
8270   private boolean groupExistsAsCompositeInOverallGroup(String groupName) {
8271    
8272     if (!groupName.endsWith(systemOfRecordExtensionSuffix())) {
8273       return false;
8274     }
8275     
8276     // find overall group and it must not be null
8277     String overallGroupName = groupName.substring(0, groupName.length() - systemOfRecordExtensionSuffix().length());
8278     Group overallGroup = GroupFinder.findByName(GrouperSession.staticGrouperSession(), overallGroupName, false, 
8279         new QueryOptions().secondLevelCache(false));
8280     
8281     boolean found = false;
8282     // system of record must be in one of the composite members.
8283     Composite composite = overallGroup != null ? overallGroup.getComposite(false) : null;
8284     if (composite != null) {
8285       return composite.getLeftGroup().hasMember(this.toSubject());
8286 
8287     }
8288     
8289     return found;
8290   }
8291   
8292   /**
8293    * change include/exclude if group changed
8294    * @param oldDisplayProperties
8295    * @param newDisplayProperties
8296    */
8297   private void changeDisplayPropertiesForIncludeExcludeType(DisplayProperties oldDisplayProperties, DisplayProperties newDisplayProperties) {
8298     
8299     changeDisplayPropertiesForOverallGroup(oldDisplayProperties, newDisplayProperties);
8300     changeDisplayPropertiesForSystemForRecordGroup(oldDisplayProperties, newDisplayProperties);
8301     changeDisplayPropertiesForIncludeGroup(oldDisplayProperties, newDisplayProperties);
8302     changeDisplayPropertiesForExcludeGroup(oldDisplayProperties, newDisplayProperties);
8303     changeDisplayPropertiesForSystemOfRecordIncludesGroup(oldDisplayProperties, newDisplayProperties);
8304     
8305   }
8306   
8307   /**
8308    * change require group if group changed
8309    * @param oldDisplayProperties
8310    * @param newDisplayProperties
8311    */
8312   private void changeDisplayPropertiesForRequireInGroupsType(DisplayProperties oldDisplayProperties, DisplayProperties newDisplayProperties) {
8313     
8314     changeDisplayPropertiesForOverallGroup(oldDisplayProperties, newDisplayProperties);
8315     changeDisplayPropertiesForSystemForRecordGroup(oldDisplayProperties, newDisplayProperties);
8316     
8317   }
8318   
8319   /**
8320    * change overall group if group changed
8321    * @param oldDisplayProperties
8322    * @param newDisplayProperties
8323    */
8324   private void changeDisplayPropertiesForOverallGroup(DisplayProperties oldDisplayProperties, DisplayProperties newDisplayProperties) {
8325     
8326     Group overallGroup = GroupFinder.findByName(GrouperSession.staticGrouperSession(), oldDisplayProperties.name, false, 
8327         new QueryOptions().secondLevelCache(false));
8328     
8329     if (overallGroup != null && !overallGroup.getUuid().equals(this.getUuid())) {
8330       
8331       boolean changed = false;
8332       
8333       if (!overallGroup.getDisplayExtension().equals(newDisplayProperties.displayExtension)) {
8334         overallGroup.setDisplayExtension(newDisplayProperties.displayExtension);
8335         changed = true;
8336 
8337         //dont change description if someone edited it
8338         String previousDescription = overallDescription(oldDisplayProperties.extension, oldDisplayProperties.displayExtension);
8339         if (StringUtils.equals(previousDescription, overallGroup.getDescription())) {
8340           String overallDescription = overallDescription(newDisplayProperties.extension, newDisplayProperties.displayExtension);
8341           overallGroup.setDescription(overallDescription);
8342         }
8343       }
8344       
8345       if (!overallGroup.getName().equals(newDisplayProperties.name)) {
8346         overallGroup.setExtension(newDisplayProperties.extension);
8347         changed = true;
8348       }
8349       
8350       if (changed) {
8351         GrouperDAOFactory.getFactory().getGroup().update(overallGroup);
8352       } 
8353       
8354     }
8355 
8356   }
8357 
8358   /**
8359    * change system of record if group changed
8360    * @param oldDisplayProperties
8361    * @param newDisplayProperties
8362    */
8363   private void changeDisplayPropertiesForSystemForRecordGroup(DisplayProperties oldDisplayProperties, DisplayProperties newDisplayProperties) {
8364     
8365     Group systemOfRecordGroup = GroupFinder.findByName(GrouperSession.staticGrouperSession(), 
8366         oldDisplayProperties.name+systemOfRecordExtensionSuffix(), false, 
8367         new QueryOptions().secondLevelCache(false));
8368     
8369     if (systemOfRecordGroup != null && !systemOfRecordGroup.getUuid().equals(this.getUuid()) && 
8370         systemOfRecordGroup.getDisplayExtension().endsWith(systemOfRecordDisplayExtensionSuffix())) {
8371       
8372       String displayExtensionWithoutSystemOfRecord = systemOfRecordGroup.getDisplayExtension().
8373         substring(0, systemOfRecordGroup.getDisplayExtension().length() - systemOfRecordDisplayExtensionSuffix().length());
8374       
8375       boolean changed = false;
8376       
8377       if (!displayExtensionWithoutSystemOfRecord.equals(newDisplayProperties.displayExtension)) {
8378         systemOfRecordGroup.setDisplayExtension(newDisplayProperties.displayExtension+systemOfRecordDisplayExtensionSuffix());
8379         changed = true;
8380 
8381         //dont change description if someone edited it
8382         String previousSystemOrRecordDescription = systemOfRecordDescription(oldDisplayProperties.extension, oldDisplayProperties.displayExtension);
8383         if (StringUtils.equals(previousSystemOrRecordDescription, systemOfRecordGroup.getDescription())) {
8384           String systemOfRecordDescription = systemOfRecordDescription(newDisplayProperties.extension, newDisplayProperties.displayExtension);
8385           systemOfRecordGroup.setDescription(systemOfRecordDescription);
8386         }
8387       }
8388       if (!newDisplayProperties.name.equals(oldDisplayProperties.name)) {
8389         systemOfRecordGroup.setExtension(newDisplayProperties.extension+systemOfRecordExtensionSuffix());
8390         changed = true;
8391       }
8392         
8393       if(changed) {
8394         GrouperDAOFactory.getFactory().getGroup().update(systemOfRecordGroup);
8395       }
8396       
8397     }
8398     
8399   }
8400 
8401   /**
8402    * change include group if group changed
8403    * @param oldDisplayProperties
8404    * @param newDisplayProperties
8405    */
8406   private void changeDisplayPropertiesForIncludeGroup(DisplayProperties oldDisplayProperties, DisplayProperties newDisplayProperties) {
8407     
8408     Group includeGroup = GroupFinder.findByName(GrouperSession.staticGrouperSession(), 
8409         oldDisplayProperties.name+includeExtensionSuffix(), false, 
8410         new QueryOptions().secondLevelCache(false));
8411     if (includeGroup != null && !includeGroup.getUuid().equals(this.getUuid()) && 
8412         includeGroup.getDisplayExtension().endsWith(includeDisplayExtensionSuffix())) {
8413       
8414       String displayExtensionWithoutInclude = includeGroup.getDisplayExtension().
8415           substring(0, includeGroup.getDisplayExtension().length() - includeDisplayExtensionSuffix().length());
8416       
8417       boolean changed = false;
8418       
8419       if (!displayExtensionWithoutInclude.equals(newDisplayProperties.displayExtension)) {
8420         includeGroup.setDisplayExtension(newDisplayProperties.displayExtension+includeDisplayExtensionSuffix());
8421         changed = true;
8422         //dont change description if someone edited it
8423         String previousIncludesDescription = includeDescription(oldDisplayProperties.extension, oldDisplayProperties.displayExtension);
8424         if (StringUtils.equals(previousIncludesDescription, includeGroup.getDescription())) {
8425           String includesDescription = includeDescription(newDisplayProperties.extension, newDisplayProperties.displayExtension);
8426           includeGroup.setDescription(includesDescription);
8427         }
8428       }
8429       if (!oldDisplayProperties.name.equals(newDisplayProperties.name)) {
8430         includeGroup.setExtension(newDisplayProperties.extension+includeExtensionSuffix());
8431         changed = true;
8432       }
8433       
8434       if (changed) {
8435         GrouperDAOFactory.getFactory().getGroup().update(includeGroup);
8436       }
8437       
8438     }
8439     
8440   }
8441 
8442   /**
8443    * change exclude group
8444    * @param oldDisplayProperties
8445    * @param newDisplayProperties
8446    */
8447   private void changeDisplayPropertiesForExcludeGroup(DisplayProperties oldDisplayProperties, DisplayProperties newDisplayProperties) {
8448         
8449     Group excludeGroup = GroupFinder.findByName(GrouperSession.staticGrouperSession(), 
8450         oldDisplayProperties.name+excludeExtensionSuffix(), false, 
8451         new QueryOptions().secondLevelCache(false));
8452     if (excludeGroup != null && !excludeGroup.getUuid().equals(this.getUuid()) && 
8453         excludeGroup.getDisplayExtension().endsWith(excludeDisplayExtensionSuffix())) {
8454       
8455       String displayExtensionWithoutExclude = excludeGroup.getDisplayExtension().
8456           substring(0, excludeGroup.getDisplayExtension().length() - excludeDisplayExtensionSuffix().length());
8457       
8458       boolean changed = false;
8459       
8460       if (!displayExtensionWithoutExclude.equals(newDisplayProperties.displayExtension)) {
8461         excludeGroup.setDisplayExtension(newDisplayProperties.displayExtension+excludeDisplayExtensionSuffix());
8462         changed = true;
8463         //dont change description if someone edited it
8464         String previousExcludesDescription = excludeDescription(oldDisplayProperties.extension, oldDisplayProperties.displayExtension);
8465         if (StringUtils.equals(previousExcludesDescription, excludeGroup.getDescription())) {
8466           String excludesDescription = excludeDescription(newDisplayProperties.extension, newDisplayProperties.displayExtension);
8467           excludeGroup.setDescription(excludesDescription);
8468         }
8469       }
8470       if (!newDisplayProperties.name.equals(oldDisplayProperties.name)) {
8471         excludeGroup.setExtension(newDisplayProperties.extension+excludeExtensionSuffix());
8472         changed = true;
8473       }
8474       
8475       if (changed) {
8476         GrouperDAOFactory.getFactory().getGroup().update(excludeGroup);
8477       }
8478       
8479     }
8480     
8481   }
8482   
8483   /**
8484    * change system of record and includes
8485    * @param oldDisplayProperties
8486    * @param newDisplayProperties
8487    */
8488   private void changeDisplayPropertiesForSystemOfRecordIncludesGroup(DisplayProperties oldDisplayProperties, DisplayProperties newDisplayProperties) {
8489     
8490     Group systemOfRecordAndIncludes  = GroupFinder.findByName(GrouperSession.staticGrouperSession(), 
8491                                                      oldDisplayProperties.name+systemOfRecordAndIncludesExtensionSuffix(), false, 
8492                                                      new QueryOptions().secondLevelCache(false));
8493     if (systemOfRecordAndIncludes != null && !systemOfRecordAndIncludes.getUuid().equals(this.getUuid()) && 
8494         systemOfRecordAndIncludes.getDisplayExtension().endsWith(systemOfRecordAndIncludesDisplayExtensionSuffix())) {
8495       
8496       String displayExtensionWithoutSystemOfRecordInclude = systemOfRecordAndIncludes.getDisplayExtension().
8497           substring(0, systemOfRecordAndIncludes.getDisplayExtension().length() - systemOfRecordAndIncludesDisplayExtensionSuffix().length());
8498       
8499       boolean changed = false;
8500       
8501       if (!displayExtensionWithoutSystemOfRecordInclude.equals(newDisplayProperties.displayExtension)) {
8502         systemOfRecordAndIncludes.setDisplayExtension(newDisplayProperties.displayExtension+systemOfRecordAndIncludesDisplayExtensionSuffix());
8503         changed = true;
8504         //dont change description if someone edited it
8505         String previousSystemOrRecordAndIncludesDescription = systemOfRecordAndIncludesDescription(oldDisplayProperties.extension, oldDisplayProperties.displayExtension);
8506         if (StringUtils.equals(previousSystemOrRecordAndIncludesDescription, systemOfRecordAndIncludes.getDescription())) {
8507           String systemOfRecordAndIncludesDescription = systemOfRecordAndIncludesDescription(newDisplayProperties.extension, newDisplayProperties.displayExtension);
8508           systemOfRecordAndIncludes.setDescription(systemOfRecordAndIncludesDescription);
8509         }
8510       }
8511       if (!newDisplayProperties.name.equals(oldDisplayProperties.name)) {
8512         systemOfRecordAndIncludes.setExtension(newDisplayProperties.extension+systemOfRecordAndIncludesExtensionSuffix());
8513         changed = true;
8514       }
8515       
8516       if (changed) {
8517         GrouperDAOFactory.getFactory().getGroup().update(systemOfRecordAndIncludes);
8518       }
8519       
8520     }
8521     
8522   }
8523   
8524   /**
8525    * Delete all direct memberships Groups Registry.  And remove composites
8526    * <pre class="eg">
8527    * try {
8528    *   g.deleteAllMemberships();
8529    * }
8530    * catch (GroupDeleteException e0) {
8531    *   // Unable to delete group
8532    * }
8533    * catch (InsufficientPrivilegeException e1) {
8534    *   // Not privileged to delete this group
8535    * }
8536    * </pre>
8537    * @throws  GroupDeleteException
8538    * @throws  InsufficientPrivilegeException
8539    */
8540   public void deleteAllMemberships() throws GroupDeleteException, InsufficientPrivilegeException {
8541     final String errorMessageSuffix = ", stem name: " + this.name + ", group extension: " + this.extension
8542       + ", group dExtension: " + this.displayExtension + ", uuid: " + this.uuid + ", ";
8543   
8544     HibernateSession.callbackHibernateSession(
8545         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
8546         new HibernateHandler() {
8547   
8548           public Object callback(HibernateHandlerBean hibernateHandlerBean)
8549               throws GrouperDAOException {
8550   
8551             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
8552   
8553             StopWatch sw = new StopWatch();
8554             sw.start();
8555             GrouperSession.validate( GrouperSession.staticGrouperSession() );
8556 
8557             if ( !PrivilegeHelper.canUpdate( GrouperSession.staticGrouperSession(), Group.this, 
8558                 GrouperSession.staticGrouperSession().getSubject() ) ) {
8559               throw new InsufficientPrivilegeException(
8560                   E.CANNOT_UPDATE + errorMessageSuffix);
8561             }
8562   
8563             if (Group.this.getTypeOfGroup() == TypeOfGroup.entity) {
8564               throw new RuntimeException("Cant delete memberships from entity, must be a group or role");
8565             }
8566             
8567             // ... And delete composite mship if it exists
8568             if (Group.this.hasComposite()) {
8569               Group.this.deleteCompositeMember();
8570             }
8571 
8572             // ... And delete all memberships - as root
8573             // Deletes (and saves) now happen within internal_deleteAllFieldType().  See GRP-254.
8574             GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
8575               
8576               public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
8577                 
8578                 Membership.internal_deleteAllFieldType( 
8579                   GrouperSession.staticGrouperSession().internal_getRootSession(), Group.this, FieldType.LIST );
8580                 
8581                 return null;
8582               }
8583             });
8584 
8585             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
8586               AuditEntry auditEntry = null;
8587               auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_DELETE_ALL_MEMBERSHIPS, "id", 
8588                   Group.this.getUuid(), "name", Group.this.getName(), "parentStemId", Group.this.getParentUuid(), 
8589                   "displayName", Group.this.getDisplayName(), "description", Group.this.getDescription());
8590               auditEntry.setDescription("Deleted all group memberships: " + Group.this.getName());
8591                 
8592               auditEntry.saveOrUpdate(true);
8593             }
8594             
8595             sw.stop();
8596             EventLog.info(GrouperSession.staticGrouperSession(), M.GROUP_ALL_MEMBERSHIPS_DEL + Quote.single(Group.this.getName()), sw);
8597             return null;
8598           }
8599         });
8600   }
8601   
8602   /**
8603    * fix enabled and disabled groups, and return the count of how many were fixed
8604    * @return the number of records affected
8605    */
8606   public static int internal_fixEnabledDisabled() {
8607     Set<Group> groups = GrouperDAOFactory.getFactory().getGroup().findAllEnabledDisabledMismatch();
8608     for (Group group : groups) {
8609       group.setEnabled(group.internal_isEnabledUsingTimestamps());
8610       group.store();
8611     }
8612 
8613     return groups.size();
8614   }
8615 
8616   /**
8617    * keep track of properties of pre/post update
8618    */
8619   private class DisplayProperties {
8620     
8621     /**
8622      * group name
8623      */
8624     private String name;
8625     
8626     /**
8627      * display extension
8628      */
8629     private String displayExtension;
8630     
8631     /**
8632      * extension
8633      */
8634     private String extension;
8635     
8636     /**
8637      * construct with new or old group
8638      * @param group
8639      */
8640     private DisplayProperties(Group group) {
8641       this.name = group.getName();
8642       this.displayExtension = group.getDisplayExtension();
8643       this.extension = group.getExtension();
8644       
8645       // for loader, addIncludeExclude type is added to the _systemOfRecord group and for UI addIncludeExclude type is
8646       // added to overall Group
8647       if (this.name.endsWith(systemOfRecordExtensionSuffix())) {
8648         this.name = this.name.substring(0, this.name.length() - systemOfRecordExtensionSuffix().length());
8649       }
8650       
8651       if (this.displayExtension.endsWith(systemOfRecordDisplayExtensionSuffix())) {
8652         this.displayExtension = this.displayExtension.substring(0, this.displayExtension.length() - systemOfRecordDisplayExtensionSuffix().length());
8653       }
8654       
8655       if (this.extension.endsWith(systemOfRecordExtensionSuffix())) {
8656         this.extension = this.extension.substring(0, this.extension.length() - systemOfRecordExtensionSuffix().length());
8657       }
8658     }
8659     
8660   }
8661   
8662 }