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  import java.sql.Timestamp;
35  import java.util.Collections;
36  import java.util.Date;
37  import java.util.HashMap;
38  import java.util.HashSet;
39  import java.util.Iterator;
40  import java.util.LinkedHashSet;
41  import java.util.LinkedList;
42  import java.util.Map;
43  import java.util.Set;
44  
45  import org.apache.commons.lang.StringUtils;
46  import org.apache.commons.lang.builder.EqualsBuilder;
47  import org.apache.commons.lang.builder.HashCodeBuilder;
48  import org.apache.commons.lang.builder.ToStringBuilder;
49  import org.apache.commons.lang.time.StopWatch;
50  import org.apache.commons.logging.Log;
51  import org.hibernate.type.LongType;
52  
53  import edu.internet2.middleware.grouper.annotations.GrouperIgnoreClone;
54  import edu.internet2.middleware.grouper.annotations.GrouperIgnoreDbVersion;
55  import edu.internet2.middleware.grouper.annotations.GrouperIgnoreFieldConstant;
56  import edu.internet2.middleware.grouper.app.deprovisioning.GrouperDeprovisioningLogic;
57  import edu.internet2.middleware.grouper.app.loader.GrouperLoader;
58  import edu.internet2.middleware.grouper.attr.AttributeDef;
59  import edu.internet2.middleware.grouper.attr.AttributeDefName;
60  import edu.internet2.middleware.grouper.attr.AttributeDefType;
61  import edu.internet2.middleware.grouper.attr.assign.AttributeAssign;
62  import edu.internet2.middleware.grouper.attr.assign.AttributeAssignStemDelegate;
63  import edu.internet2.middleware.grouper.attr.assign.AttributeAssignable;
64  import edu.internet2.middleware.grouper.attr.finder.AttributeDefFinder;
65  import edu.internet2.middleware.grouper.attr.finder.AttributeDefNameFinder;
66  import edu.internet2.middleware.grouper.attr.value.AttributeValueDelegate;
67  import edu.internet2.middleware.grouper.audit.AuditEntry;
68  import edu.internet2.middleware.grouper.audit.AuditTypeBuiltin;
69  import edu.internet2.middleware.grouper.cache.GrouperCache;
70  import edu.internet2.middleware.grouper.cfg.GrouperConfig;
71  import edu.internet2.middleware.grouper.changeLog.ChangeLogEntry;
72  import edu.internet2.middleware.grouper.changeLog.ChangeLogLabels;
73  import edu.internet2.middleware.grouper.changeLog.ChangeLogTempToEntity;
74  import edu.internet2.middleware.grouper.changeLog.ChangeLogTypeBuiltin;
75  import edu.internet2.middleware.grouper.exception.AttributeDefAddException;
76  import edu.internet2.middleware.grouper.exception.AttributeDefNameAddException;
77  import edu.internet2.middleware.grouper.exception.GrantPrivilegeAlreadyExistsException;
78  import edu.internet2.middleware.grouper.exception.GrantPrivilegeException;
79  import edu.internet2.middleware.grouper.exception.GroupAddAlreadyExistsException;
80  import edu.internet2.middleware.grouper.exception.GroupAddException;
81  import edu.internet2.middleware.grouper.exception.GroupNotFoundException;
82  import edu.internet2.middleware.grouper.exception.GrouperException;
83  import edu.internet2.middleware.grouper.exception.GrouperSessionException;
84  import edu.internet2.middleware.grouper.exception.GrouperValidationException;
85  import edu.internet2.middleware.grouper.exception.InsufficientPrivilegeException;
86  import edu.internet2.middleware.grouper.exception.MemberNotFoundException;
87  import edu.internet2.middleware.grouper.exception.RevokePrivilegeAlreadyRevokedException;
88  import edu.internet2.middleware.grouper.exception.RevokePrivilegeException;
89  import edu.internet2.middleware.grouper.exception.SchemaException;
90  import edu.internet2.middleware.grouper.exception.StemAddAlreadyExistsException;
91  import edu.internet2.middleware.grouper.exception.StemAddException;
92  import edu.internet2.middleware.grouper.exception.StemDeleteException;
93  import edu.internet2.middleware.grouper.exception.StemModifyException;
94  import edu.internet2.middleware.grouper.exception.StemNotFoundException;
95  import edu.internet2.middleware.grouper.exception.UnableToPerformAlreadyExistsException;
96  import edu.internet2.middleware.grouper.exception.UnableToPerformException;
97  import edu.internet2.middleware.grouper.group.TypeOfGroup;
98  import edu.internet2.middleware.grouper.grouperSet.GrouperSetElement;
99  import edu.internet2.middleware.grouper.hibernate.AuditControl;
100 import edu.internet2.middleware.grouper.hibernate.GrouperTransaction;
101 import edu.internet2.middleware.grouper.hibernate.GrouperTransactionHandler;
102 import edu.internet2.middleware.grouper.hibernate.GrouperTransactionType;
103 import edu.internet2.middleware.grouper.hibernate.HibUtils;
104 import edu.internet2.middleware.grouper.hibernate.HibernateHandler;
105 import edu.internet2.middleware.grouper.hibernate.HibernateHandlerBean;
106 import edu.internet2.middleware.grouper.hibernate.HibernateSession;
107 import edu.internet2.middleware.grouper.hooks.StemHooks;
108 import edu.internet2.middleware.grouper.hooks.beans.HooksStemBean;
109 import edu.internet2.middleware.grouper.hooks.logic.GrouperHookType;
110 import edu.internet2.middleware.grouper.hooks.logic.GrouperHooksUtils;
111 import edu.internet2.middleware.grouper.hooks.logic.HookVeto;
112 import edu.internet2.middleware.grouper.hooks.logic.VetoTypeGrouper;
113 import edu.internet2.middleware.grouper.instrumentation.InstrumentationDataBuiltinTypes;
114 import edu.internet2.middleware.grouper.instrumentation.InstrumentationThread;
115 import edu.internet2.middleware.grouper.internal.dao.GrouperDAOException;
116 import edu.internet2.middleware.grouper.internal.dao.QueryOptions;
117 import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3AttributeAssignActionDAO;
118 import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3AttributeAssignDAO;
119 import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3AttributeDefDAO;
120 import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3AttributeDefNameDAO;
121 import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GrouperVersioned;
122 import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3MemberDAO;
123 import edu.internet2.middleware.grouper.internal.util.GrouperUuid;
124 import edu.internet2.middleware.grouper.internal.util.ParameterHelper;
125 import edu.internet2.middleware.grouper.internal.util.Quote;
126 import edu.internet2.middleware.grouper.internal.util.U;
127 import edu.internet2.middleware.grouper.log.EventLog;
128 import edu.internet2.middleware.grouper.misc.E;
129 import edu.internet2.middleware.grouper.misc.GrouperDAOFactory;
130 import edu.internet2.middleware.grouper.misc.GrouperHasContext;
131 import edu.internet2.middleware.grouper.misc.GrouperObject;
132 import edu.internet2.middleware.grouper.misc.GrouperSessionHandler;
133 import edu.internet2.middleware.grouper.misc.GrouperVersion;
134 import edu.internet2.middleware.grouper.misc.M;
135 import edu.internet2.middleware.grouper.misc.Owner;
136 import edu.internet2.middleware.grouper.misc.SaveMode;
137 import edu.internet2.middleware.grouper.permissions.role.Role;
138 import edu.internet2.middleware.grouper.permissions.role.RoleHierarchyType;
139 import edu.internet2.middleware.grouper.permissions.role.RoleSet;
140 import edu.internet2.middleware.grouper.pit.PITStem;
141 import edu.internet2.middleware.grouper.pit.PITUtils;
142 import edu.internet2.middleware.grouper.privs.AccessPrivilege;
143 import edu.internet2.middleware.grouper.privs.AttributeDefPrivilege;
144 import edu.internet2.middleware.grouper.privs.NamingPrivilege;
145 import edu.internet2.middleware.grouper.privs.Privilege;
146 import edu.internet2.middleware.grouper.privs.PrivilegeHelper;
147 import edu.internet2.middleware.grouper.rules.RuleApi;
148 import edu.internet2.middleware.grouper.rules.RuleCheckType;
149 import edu.internet2.middleware.grouper.rules.RuleDefinition;
150 import edu.internet2.middleware.grouper.rules.RuleEngine;
151 import edu.internet2.middleware.grouper.rules.RuleIfConditionEnum;
152 import edu.internet2.middleware.grouper.rules.RuleUtils;
153 import edu.internet2.middleware.grouper.rules.beans.RulesAttributeDefBean;
154 import edu.internet2.middleware.grouper.rules.beans.RulesGroupBean;
155 import edu.internet2.middleware.grouper.rules.beans.RulesPrivilegeBean;
156 import edu.internet2.middleware.grouper.rules.beans.RulesStemBean;
157 import edu.internet2.middleware.grouper.stem.StemSet;
158 import edu.internet2.middleware.grouper.subj.GrouperSubject;
159 import edu.internet2.middleware.grouper.tableIndex.TableIndex;
160 import edu.internet2.middleware.grouper.tableIndex.TableIndexType;
161 import edu.internet2.middleware.grouper.util.GrouperUtil;
162 import edu.internet2.middleware.grouper.util.PerformanceLogger;
163 import edu.internet2.middleware.grouper.validator.AddAlternateStemNameValidator;
164 import edu.internet2.middleware.grouper.validator.AddAttributeDefNameValidator;
165 import edu.internet2.middleware.grouper.validator.AddAttributeDefValidator;
166 import edu.internet2.middleware.grouper.validator.AddGroupValidator;
167 import edu.internet2.middleware.grouper.validator.AddStemValidator;
168 import edu.internet2.middleware.grouper.validator.DeleteStemValidator;
169 import edu.internet2.middleware.grouper.validator.GrouperValidator;
170 import edu.internet2.middleware.grouper.validator.NamingValidator;
171 import edu.internet2.middleware.grouper.validator.NotNullOrEmptyValidator;
172 import edu.internet2.middleware.grouper.xml.export.XmlExportStem;
173 import edu.internet2.middleware.grouper.xml.export.XmlImportable;
174 import edu.internet2.middleware.subject.SourceUnavailableException;
175 import edu.internet2.middleware.subject.Subject;
176 import edu.internet2.middleware.subject.SubjectNotFoundException;
177 
178 /** 
179  * A namespace within the Groups Registry.
180  * <p/>
181  * @author  blair christensen.
182  * @version $Id: Stem.java,v 1.209 2009-12-15 06:47:06 mchyzer Exp $
183  */
184 @SuppressWarnings("serial")
185 public class Stem extends GrouperAPI implements GrouperHasContext, Owner, 
186     Hib3GrouperVersioned, Comparable<GrouperObject>, XmlImportable<Stem>, AttributeAssignable, GrouperSetElement,
187     GrouperObject {
188 
189   /**
190    * 
191    */
192   public static final String VALIDATION_STEM_NAME_TOO_LONG_KEY = "stemNameTooLong";
193 
194   /**
195    * 
196    */
197   public static final String VALIDATION_STEM_DISPLAY_NAME_TOO_LONG_KEY = "stemDisplayNameTooLong";
198 
199   /**
200    * 
201    */
202   public static final String VALIDATION_STEM_EXTENSION_TOO_LONG_KEY = "stemExtensionTooLong";
203 
204   /**
205    * 
206    */
207   public static final String VALIDATION_STEM_DISPLAY_EXTENSION_TOO_LONG_KEY = "stemDisplayExtensionTooLong";
208 
209   /**
210    * 
211    */
212   public static final String VALIDATION_STEM_DESCRIPTION_TOO_LONG_KEY = "stemDescriptionTooLong";
213 
214   /**
215    * get the name of the parent stem
216    * @return the name of the parent stem
217    */
218   public String getParentStemName() {
219     return GrouperUtil.parentStemNameFromName(this.getName(), false);
220   } 
221 
222   /**
223    * id is same as uuid
224    * @return id
225    */
226   public String getId() {
227     return this.getUuid();
228   }
229   
230   /** table for stems table in the db */
231   public static final String TABLE_GROUPER_STEMS = "grouper_stems";
232 
233   /** uuid col in db (not used anymore) */
234   public static final String COLUMN_UUID = "uuid";
235   
236   /** col */
237   public static final String COLUMN_PARENT_STEM = "parent_stem";
238 
239   /** col */
240   public static final String COLUMN_NAME = "name";
241   
242   /** col */
243   public static final String COLUMN_DISPLAY_NAME = "display_name";
244 
245   /** col */
246   public static final String COLUMN_CREATOR_ID = "creator_id";
247 
248   /** id col in db */
249   public static final String COLUMN_ID = "id";
250 
251   /** unique number for this stem */
252   public static final String COLUMN_ID_INDEX = "id_index";
253 
254   /** id col in db */
255   public static final String COLUMN_CREATE_TIME = "create_time";
256 
257   /** id col in db */
258   public static final String COLUMN_MODIFIER_ID = "modifier_id";
259 
260   /** id col in db */
261   public static final String COLUMN_MODIFY_TIME = "modify_time";
262 
263   /** id col in db */
264   public static final String COLUMN_DISPLAY_EXTENSION = "display_extension";
265 
266   /** id col in db */
267   public static final String COLUMN_EXTENSION = "extension";
268 
269   /** id col in db */
270   public static final String COLUMN_DESCRIPTION = "description";
271 
272   /** an alternate name for this stem */
273   public static final String COLUMN_ALTERNATE_NAME = "alternate_name";
274 
275   /** column for hibernate version number */
276   public static final String COLUMN_HIBERNATE_VERSION_NUMBER = "hibernate_version_number";
277   
278   /**
279    * context id column name
280    */
281   public static final String COLUMN_CONTEXT_ID = "context_id";
282 
283 
284   
285   /** old id col for id conversion */
286   public static final String COLUMN_OLD_ID = "old_id";
287   
288   /** old uuid id col for id conversion */
289   public static final String COLUMN_OLD_UUID = "old_uuid";
290  
291   /** timestamp of the last membership change for this group */
292   public static final String COLUMN_LAST_MEMBERSHIP_CHANGE = "last_membership_change";
293 
294   /** param helper */
295   @GrouperIgnoreDbVersion 
296   @GrouperIgnoreFieldConstant
297   @GrouperIgnoreClone
298   private ParameterHelperernal/util/ParameterHelper.html#ParameterHelper">ParameterHelper param = new ParameterHelper();
299 
300 
301   //*****  START GENERATED WITH GenerateFieldConstants.java *****//
302 
303   /** constant for field name for: alternateNameDb */
304   public static final String FIELD_ALTERNATE_NAME_DB = "alternateNameDb";
305   
306   /** constant for field name for: createTime */
307   public static final String FIELD_CREATE_TIME = "createTime";
308 
309   /** constant for field name for: creatorUUID */
310   public static final String FIELD_CREATOR_UUID = "creatorUUID";
311 
312   /** constant for field name for: dbVersion */
313   public static final String FIELD_DB_VERSION = "dbVersion";
314 
315   /** constant for field name for: description */
316   public static final String FIELD_DESCRIPTION = "description";
317 
318   /** constant for field name for: displayExtension */
319   public static final String FIELD_DISPLAY_EXTENSION = "displayExtension";
320 
321   /** constant for field name for: displayName */
322   public static final String FIELD_DISPLAY_NAME = "displayName";
323 
324   /** constant for field name for: extension */
325   public static final String FIELD_EXTENSION = "extension";
326 
327   /** constant for field name for: idIndex */
328   public static final String FIELD_ID_INDEX = "idIndex";
329 
330   /** constant for field name for: modifierUUID */
331   public static final String FIELD_MODIFIER_UUID = "modifierUUID";
332 
333   /** constant for field name for: modifyTime */
334   public static final String FIELD_MODIFY_TIME = "modifyTime";
335 
336   /** constant for field name for: name */
337   public static final String FIELD_NAME = "name";
338 
339   /** constant for field name for: parentUuid */
340   public static final String FIELD_PARENT_UUID = "parentUuid";
341 
342   /** constant for field name for: uuid */
343   public static final String FIELD_UUID = "uuid";
344   
345   /** constant for field name for: lastMembershipChangeDb */
346   public static final String FIELD_LAST_MEMBERSHIP_CHANGE_DB = "lastMembershipChangeDb";
347 
348   /**
349    * fields which are included in db version
350    */
351   private static final Set<String> DB_VERSION_FIELDS = GrouperUtil.toSet(
352       FIELD_CREATE_TIME, FIELD_CREATOR_UUID, FIELD_DESCRIPTION, 
353       FIELD_DISPLAY_EXTENSION, FIELD_DISPLAY_NAME, FIELD_EXTENSION, FIELD_ID_INDEX, 
354       FIELD_MODIFIER_UUID, 
355       FIELD_MODIFY_TIME, FIELD_NAME, FIELD_PARENT_UUID, 
356       FIELD_UUID, FIELD_LAST_MEMBERSHIP_CHANGE_DB, FIELD_ALTERNATE_NAME_DB);
357 
358   /**
359    * fields which are included in clone method
360    */
361   private static final Set<String> CLONE_FIELDS = GrouperUtil.toSet(
362       FIELD_CREATE_TIME, FIELD_CREATOR_UUID, FIELD_DB_VERSION, 
363       FIELD_DESCRIPTION, FIELD_DISPLAY_EXTENSION, FIELD_DISPLAY_NAME, FIELD_EXTENSION, 
364       FIELD_HIBERNATE_VERSION_NUMBER, FIELD_ID_INDEX, FIELD_MODIFIER_UUID, FIELD_MODIFY_TIME, 
365       FIELD_NAME, FIELD_PARENT_UUID, FIELD_UUID, FIELD_LAST_MEMBERSHIP_CHANGE_DB,
366       FIELD_ALTERNATE_NAME_DB);
367 
368   //*****  END GENERATED WITH GenerateFieldConstants.java *****//
369 
370   /**
371    * @see java.lang.Comparable#compareTo(java.lang.Object)
372    */
373   public int compareTo(GrouperObject that) {
374     if (that==null) {
375       return 1;
376     }
377     String thisName = StringUtils.defaultString(this.getName());
378     String thatName = StringUtils.defaultString(that.getName());
379     return thisName.compareTo(thatName);
380   }
381 
382 
383   /**
384    * Search scope: one-level or subtree.
385    * @since   1.2.1
386    */
387   public enum Scope { 
388     /** one level (direct children) */
389     ONE, 
390     
391     /** all decendents */
392     SUB;
393     
394     /**
395      * do a case-insensitive matching
396      * 
397      * @param string
398      * @param exceptionOnNull will not allow null or blank entries
399      * @return the enum or null or exception if not found
400      */
401     public static Scope valueOfIgnoreCase(String string, boolean exceptionOnNull) {
402       return GrouperUtil.enumValueOfIgnoreCase(Scope.class, 
403           string, exceptionOnNull);
404 
405     }
406 
407     
408   }; // TODO 20070802 is this the right location?
409 
410   /**
411    * Hierarchy delimiter.
412    */
413   public static final String DELIM      = ":";
414   /**
415    * Default name of root stem.
416    */
417   public static final String ROOT_NAME  = GrouperConfig.EMPTY_STRING;
418   
419   
420   // PROTECTED CLASS CONSTANTS //
421   // TODO 20070419 how can i get rid of this?
422   /** root int */
423   public static final String ROOT_INT = ":"; // Appease Oracle, et. al.
424 
425 
426   // PRIVATE CLASS CONSTANTS //
427   /** event log */
428   private static final EventLogrouper/log/EventLog.html#EventLog">EventLog EL = new EventLog();
429 
430   // PRIVATE INSTANCE VARIABLES //
431   /** creator of stem */
432   @GrouperIgnoreDbVersion 
433   @GrouperIgnoreFieldConstant
434   @GrouperIgnoreClone
435   private Subject creator;
436   
437   /** modifier of stem */
438   @GrouperIgnoreDbVersion 
439   @GrouperIgnoreFieldConstant
440   @GrouperIgnoreClone
441   private Subject modifier;
442 
443   /** */
444   private long    createTime;
445   /** */
446   private String  creatorUUID;
447 
448   /** */
449   private String  description;
450   /** */
451   private String  displayExtension;
452   /** */
453   private String  displayName;
454   /** */
455   private String  extension;
456   /** */
457   private String  modifierUUID;
458   /** */
459   private long    modifyTime;
460   /** */
461   private String  name;
462   /** */
463   private String  parentUuid;
464   /** */
465   private String  uuid;
466   
467   /** alternate name of stem */
468   private String alternateNameDb;
469 
470   /** context id of the transaction */
471   private String contextId;
472 
473   /** */
474   @GrouperIgnoreClone @GrouperIgnoreDbVersion @GrouperIgnoreFieldConstant
475   private AttributeAssignStemDelegate attributeAssignStemDelegate;
476   
477   /**
478    * 
479    * @return the delegate
480    */
481   public AttributeAssignStemDelegate getAttributeDelegate() {
482     if (this.attributeAssignStemDelegate == null) {
483       this.attributeAssignStemDelegate = new AttributeAssignStemDelegate(this);
484     }
485     return this.attributeAssignStemDelegate;
486   }
487   
488   /** */
489   @GrouperIgnoreClone @GrouperIgnoreDbVersion @GrouperIgnoreFieldConstant
490   private AttributeValueDelegate attributeValueDelegate;
491   
492   /**
493    * this delegate works on attributes and values at the same time
494    * @return the delegate
495    */
496   public AttributeValueDelegate getAttributeValueDelegate() {
497     if (this.attributeValueDelegate == null) {
498       this.attributeValueDelegate = new AttributeValueDelegate(this.getAttributeDelegate());
499     }
500     return this.attributeValueDelegate;
501   }
502   
503 
504   /**
505    * context id of the transaction
506    * @return context id
507    */
508   public String getContextId() {
509     return this.contextId;
510   }
511 
512   /**
513    * context id of the transaction
514    * @param contextId1
515    */
516   public void setContextId(String contextId1) {
517     this.contextId = contextId1;
518   }
519 
520 
521   // PUBLIC INSTANCE METHODS //
522 
523   // PUBLIC INSTANCE METHODS //
524   
525   /**
526    * Add a new group to the registry.
527    * <pre class="eg">
528    * // Add a group with the extension "edu" beneath this stem.
529    * try {
530    *   Group edu = ns.addChildGroup("edu", "edu domain");
531    * }
532    * catch (GroupAddException eGA) {
533    *   // Group not added
534    * }
535    * catch (InsufficientPrivilegeException eIP) {
536    *   // Not privileged to add group
537    * }
538    * </pre>
539    * @param   extension         Group's extension
540    * @param   displayExtension  Groups' displayExtension
541    * @return  The added {@link Group}
542    * @throws  GroupAddException 
543    * @throws  InsufficientPrivilegeException
544    */
545   public Group addChildGroup(String extension, String displayExtension) 
546     throws  GroupAddException,
547             InsufficientPrivilegeException
548   {
549     return this.internal_addChildGroup(extension, displayExtension, null);
550   } // public Group addChildGroup(extension, displayExtension)
551 
552   /**
553    * Add a new attribute def to the registry.
554    * @param   extension attributeDef's extension
555    * @param attributeDefType 
556    * @return  The added {@link AttributeDef}
557    * @throws  InsufficientPrivilegeException
558    */
559   public AttributeDef addChildAttributeDef(String extension, 
560       AttributeDefType attributeDefType) 
561     throws InsufficientPrivilegeException {
562     return this.internal_addChildAttributeDef(GrouperSession.staticGrouperSession(true), 
563         extension, null, attributeDefType, null);
564   }
565 
566   /**
567    * Add a new attribute def to the registry.
568    * @param attributeDef is the definition of this attribute
569    * @param   extension attributeDef's extension
570    * @param displayExtension 
571    * @return  The added {@link AttributeDef}
572    * @throws  InsufficientPrivilegeException
573    */
574   public AttributeDefName addChildAttributeDefName(AttributeDef attributeDef, String extension, String displayExtension) 
575     throws InsufficientPrivilegeException {
576     return this.internal_addChildAttributeDefName(GrouperSession.staticGrouperSession(true), attributeDef,
577         extension, displayExtension, null, null);
578   }
579 
580   /**
581    * Add a new attribute def to the registry.
582    * @param attributeDef is the definition of this attribute
583    * @param   extension attributeDef's extension
584    * @param displayExtension 
585    * @param uuid 
586    * @return  The added {@link AttributeDef}
587    * @throws  InsufficientPrivilegeException
588    */
589   public AttributeDefName addChildAttributeDefName(AttributeDef attributeDef, String extension, String displayExtension, String uuid) 
590     throws InsufficientPrivilegeException {
591     return this.internal_addChildAttributeDefName(GrouperSession.staticGrouperSession(true), attributeDef,
592         extension, displayExtension, StringUtils.trimToNull(uuid), null);
593   }
594 
595   /**
596    * Add a new stem to the registry.
597    * <pre class="eg">
598    * // Add a stem with the extension "edu" beneath this stem.
599    * try {
600    *   Stem edu = ns.addChildStem("edu", "edu domain");
601    * }
602    * catch (StemAddException e) {
603    *   // Stem not added
604    * }
605    * </pre>
606    * @param   extension         Stem's extension
607    * @param   displayExtension  Stem' displayExtension
608    * @param uuid if creating this is the uuid
609    * @param failIfExists throws StemAddException if exists, else just return the existing stem
610    * @return  The added {@link Stem}
611    * @throws  InsufficientPrivilegeException
612    * @throws  StemAddException 
613    */
614   public Stem addChildStem(String extension, String displayExtension, String uuid, boolean failIfExists) 
615     throws  InsufficientPrivilegeException,
616             StemAddException {
617     return internal_addChildStem(GrouperSession.staticGrouperSession(), extension, displayExtension, uuid, true, failIfExists);
618   } 
619   
620   /**
621    * Add a new stem to the registry.
622    * <pre class="eg">
623    * // Add a stem with the extension "edu" beneath this stem.
624    * try {
625    *   Stem edu = ns.addChildStem("edu", "edu domain");
626    * }
627    * catch (StemAddException e) {
628    *   // Stem not added
629    * }
630    * </pre>
631    * @param   extension         Stem's extension
632    * @param   displayExtension  Stem' displayExtension
633    * @return  The added {@link Stem}
634    * @throws  InsufficientPrivilegeException
635    * @throws  StemAddException 
636    */
637   public Stem addChildStem(String extension, String displayExtension) 
638     throws  InsufficientPrivilegeException,
639             StemAddException {
640     return internal_addChildStem(extension, displayExtension, null);
641   } 
642 
643   /**
644    * keep track of if we are in a delete so hooks can 
645    */
646   private static ThreadLocal<Boolean> threadLocalInStemDelete = new InheritableThreadLocal<Boolean>();
647   
648   /**
649    * see if we are in the middle of a delete (e.g. for hook)
650    * @return true if delete is occurring
651    */
652   public static boolean deleteOccuring() {
653     Boolean deleteOccuring = threadLocalInStemDelete.get();
654     if (deleteOccuring != null) {
655       return deleteOccuring;
656     }
657     return false;
658   }
659 
660   /**
661    * Delete this stem from the Groups Registry.
662    * <pre class="eg">
663    * try {
664    *   ns.delete();
665    * }
666    * catch (InsufficientPrivilegeException eIP) {
667    *   // not privileged to delete stem
668    * }
669    * catch (StemDeleteException eSD) {
670    *   // unable to delete stem
671    * }
672    * </pre>
673    * @throws  InsufficientPrivilegeException
674    * @throws  StemDeleteException
675    */
676   public void delete() throws InsufficientPrivilegeException, StemDeleteException {
677     
678     //this is no longer recently created
679     stemCreatedCache().remove(this.name);
680     
681     HibernateSession.callbackHibernateSession(
682         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
683         new HibernateHandler() {
684 
685           public Object callback(HibernateHandlerBean hibernateHandlerBean)
686               throws GrouperDAOException {
687 
688             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
689 
690             StopWatch sw = new StopWatch();
691             sw.start();
692             GrouperSession.validate(GrouperSession.staticGrouperSession());
693             if ( !PrivilegeHelper.canStemAdmin( Stem.this, GrouperSession.staticGrouperSession().getSubject() ) ) {
694               throw new InsufficientPrivilegeException(E.CANNOT_STEM_ADMIN + ", " + Stem.this.getName());
695             }
696             DeleteStemValidator v = DeleteStemValidator.validate(Stem.this);
697             if (v.isInvalid()) {
698               throw new StemDeleteException( v.getErrorMessage() );
699             }
700             try {
701               threadLocalInStemDelete.set(true);
702               String name = Stem.this.getName(); // Preserve name for logging
703               Stem.this._revokeAllNamingPrivs();
704               
705               //delete any attributes on this stem, this is done as root
706               GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
707                 
708                 public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
709                   //delete any attributes on this stem
710                   Set<AttributeAssign> attributeAssigns = GrouperDAOFactory.getFactory().getAttributeAssign().findByOwnerStemId(Stem.this.getUuid());
711                   
712                   for (AttributeAssign attributeAssign : attributeAssigns) {
713                     attributeAssign.delete();
714                   }
715                   return null;
716                 }
717               });
718 
719               GrouperDAOFactory.getFactory().getStem().delete( Stem.this );
720               
721               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
722                 AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.STEM_DELETE, "id", 
723                     Stem.this.getUuid(), "name", Stem.this.getName(), "parentStemId", Stem.this.getUuid(), "displayName", 
724                     Stem.this.getDisplayName(), "description", Stem.this.getDescription());
725                 auditEntry.setDescription("Deleted stem: " +Stem.this.getName());
726                 auditEntry.saveOrUpdate(true);
727               }
728               
729               sw.stop();
730               EventLog.info(GrouperSession.staticGrouperSession(), M.STEM_DEL + Quote.single(name), sw);
731             }
732             catch (GrouperDAOException eDAO)      {
733               throw new StemDeleteException( eDAO.getMessage() + ", " + Stem.this.getName(), eDAO );
734             }
735             catch (RevokePrivilegeException eRP)  {
736               throw new StemDeleteException(eRP.getMessage() + ", " + Stem.this.getName(), eRP);
737             }
738             catch (SchemaException eS)            {
739               throw new StemDeleteException(eS.getMessage() + ", " + Stem.this.getName(), eS);
740             } finally {
741               threadLocalInStemDelete.remove();
742             }
743             return null;
744          }
745     });
746 
747     
748   }
749 
750   /**
751    * Get groups that are immediate children of this stem.
752    * @return  Set of {@link Group} objects.
753    * @see     Stem#getChildGroups(Scope)
754    */
755   public Set getChildGroups() {
756     return this.getChildGroups(Scope.ONE);
757   }
758 
759   /**
760    * Get groups that are children of this stem.
761    * @param   scope of search: <code>Scope.ONE</code> or <code>Scope.SUB</code>
762    * @return  Child groups.
763    * @throws  IllegalArgumentException if null scope.
764    * @since   1.2.1
765    */
766   public Set<Group> getChildGroups(Scope scope) 
767     throws  IllegalArgumentException {
768     return getChildGroups(scope, AccessPrivilege.VIEW_PRIVILEGES, null);
769   }
770 
771   /**
772    * Get groups that are children of this stem.
773    * @param   scope of search: <code>Scope.ONE</code> or <code>Scope.SUB</code>
774    * @param inPrivSet set of privileges that the grouper session needs one of for the row to be returned.
775    * AccessPrivilege has some pre-baked constant sets for use here
776    * @param queryOptions 
777    * @return  Child groups.
778    * @throws  IllegalArgumentException if null scope.
779    * @since   1.2.1
780    */
781   public Set<Group> getChildGroups(Scope scope, Set<Privilege> inPrivSet, QueryOptions queryOptions) {
782     return this.getChildGroups(scope, inPrivSet, queryOptions, null);
783   }
784 
785   /**
786    * Get groups that are children of this stem.
787    * @param   scope of search: <code>Scope.ONE</code> or <code>Scope.SUB</code>
788    * @param inPrivSet set of privileges that the grouper session needs one of for the row to be returned.
789    * AccessPrivilege has some pre-baked constant sets for use here
790    * @param typeOfGroups is the type of groups to get, or null for all
791    * @param queryOptions 
792    * @return  Child groups.
793    * @throws  IllegalArgumentException if null scope.
794    * @since   1.2.1
795    */
796   public Set<Group> getChildGroups(Scope scope, Set<Privilege> inPrivSet, QueryOptions queryOptions, Set<TypeOfGroup> typeOfGroups) 
797     throws  IllegalArgumentException {
798     return this.getChildGroups(scope, inPrivSet, queryOptions, typeOfGroups, true);
799   }
800   
801   /**
802    * Get groups that are children of this stem.
803    * @param   scope of search: <code>Scope.ONE</code> or <code>Scope.SUB</code>
804    * @param inPrivSet set of privileges that the grouper session needs one of for the row to be returned.
805    * AccessPrivilege has some pre-baked constant sets for use here
806    * @param typeOfGroups is the type of groups to get, or null for all
807    * @param queryOptions 
808    * @param enabled true if enabled only, false if disabled only, null if everything
809    * @return  Child groups.
810    * @throws  IllegalArgumentException if null scope.
811    * @since   1.2.1
812    */
813   public Set<Group> getChildGroups(Scope scope, Set<Privilege> inPrivSet, QueryOptions queryOptions, Set<TypeOfGroup> typeOfGroups, Boolean enabled) 
814     throws  IllegalArgumentException {
815     if (scope == null) { // TODO 20070815 ParameterHelper
816       throw new IllegalArgumentException("null Scope");
817     }
818     this.param.notNullPrivilegeSet(inPrivSet);
819 
820     inPrivSet = AccessPrivilege.filter(inPrivSet);
821     
822     if (inPrivSet.size() == 0) {
823       return new LinkedHashSet<Group>();
824     }
825     
826     GrouperSession grouperSession = GrouperSession.staticGrouperSession();
827     Subject     subj    = grouperSession.getSubject();
828     Set<Group> findAllChildGroups = 
829       GrouperDAOFactory.getFactory().getStem().findAllChildGroupsSecure( this, scope, 
830           grouperSession, subj, inPrivSet, queryOptions, typeOfGroups, enabled );
831     return findAllChildGroups;
832   }
833 
834   /**
835    * Get groups that are children of this stem and there is a list membership.
836    * @param   scope of search: <code>Scope.ONE</code> or <code>Scope.SUB</code>
837    * @param inPrivSet set of privileges that the grouper session needs one of for the row to be returned.
838    * AccessPrivilege has some pre-baked constant sets for use here
839    * @param queryOptions 
840    * @return  Child groups.
841    * @throws  IllegalArgumentException if null scope.
842    * @since   1.2.1
843    */
844   public Set<Group> getChildMembershipGroups(Scope scope, Set<Privilege> inPrivSet, QueryOptions queryOptions) 
845     throws  IllegalArgumentException {
846     if (scope == null) { // TODO 20070815 ParameterHelper
847       throw new IllegalArgumentException("null Scope");
848     }
849     this.param.notNullPrivilegeSet(inPrivSet);
850       
851     GrouperSession grouperSession = GrouperSession.staticGrouperSession();
852     Subject     subj    = grouperSession.getSubject();
853     Set<Group> findAllChildGroups = 
854       GrouperDAOFactory.getFactory().getStem().findAllChildMembershipGroupsSecure( this, scope, 
855           grouperSession, subj, inPrivSet, queryOptions );
856     return findAllChildGroups;
857       }
858 
859   /**
860    * Get groups that are children of this stem.
861    * @param   scope of search: <code>Scope.ONE</code> or <code>Scope.SUB</code>
862    * @param inPrivSet set of privileges that the grouper session needs one of for the row to be returned.
863    * AccessPrivilege has some pre-baked constant sets for use here
864    * @param queryOptions 
865    * @return  Child groups.
866    * @throws  IllegalArgumentException if null scope.
867    * @since   1.2.1
868    */
869   public Set<Stem> getChildStems(Scope scope, Set<Privilege> inPrivSet, QueryOptions queryOptions) 
870     throws  IllegalArgumentException {
871     if (scope == null) { // TODO 20070815 ParameterHelper
872       throw new IllegalArgumentException("null Scope");
873     }
874     this.param.notNullPrivilegeSet(inPrivSet);
875 
876     GrouperSession grouperSession = GrouperSession.staticGrouperSession();
877     Subject     subj    = grouperSession.getSubject();
878     Set<Stem> findAllChildStems = 
879       GrouperDAOFactory.getFactory().getStem().findAllChildStemsSecure( this, scope, 
880           grouperSession, subj, inPrivSet, queryOptions );
881     return findAllChildStems;
882   }
883 
884   /**
885    * get child groups
886    * @param privileges privs 
887    * @param scope all or direct
888    * @return  Child groups where current subject has any of the specified <i>privileges</i>.
889    * @throws  IllegalArgumentException if any parameter is null.
890    * @since   1.2.1
891    * @deprecated use the overload
892    */
893   @Deprecated
894   public Set<Group> getChildGroups(Privilege[] privileges, Scope scope)
895       throws  IllegalArgumentException {
896 
897     return getChildGroups(scope, GrouperUtil.toSet(privileges), null);
898     }
899 
900   /**
901    * Get stems that are immediate children of this stem.
902    * @return  Set of {@link Stem} objects.
903    * @see     Stem#getChildStems(Scope)
904    */
905   public Set<Stem> getChildStems() {
906     return this.getChildStems(Scope.ONE);
907   }
908 
909   /**
910    * Get stems that are children of this stem.
911    * @param   scope of search: <code>Scope.ONE</code> or <code>Scope.SUB</code>
912    * @return  Child stems.
913    * @throws  IllegalArgumentException if null scope.
914    * @since   1.2.1
915    */
916   public Set<Stem> getChildStems(Scope scope) {
917     return getChildStems(scope, null);
918   }
919 
920   /**
921    * Get stems that are children of this stem.
922    * @param   scope of search: <code>Scope.ONE</code> or <code>Scope.SUB</code>
923    * @param queryOptions 
924    * @return  Child stems.
925    * @throws  IllegalArgumentException if null scope.
926    * @since   1.2.1
927    */
928   public Set<Stem> getChildStems(Scope scope, QueryOptions queryOptions) 
929     throws  IllegalArgumentException
930   {
931     if (scope == null) { // TODO 20070815 ParameterHelper
932       throw new IllegalArgumentException("null Scope");
933     }
934     Set<Stem> stems = new LinkedHashSet();
935     for ( Stem child : GrouperDAOFactory.getFactory().getStem().findAllChildStems( this, scope, queryOptions ) ) {
936       stems.add(child);
937     }
938     return stems;
939   }
940 
941   /**
942    * get child stems
943    * @param privileges privs
944    * @param scope all or direct
945    * @return  Child (or deeper) stems where current subject has any of the specified <i>privileges</i>.  Parent stems of grandchild (or deeper) groups where the current subject has any of the specified <i>privileges</i>.
946    * @throws  IllegalArgumentException if any parameter is null.
947    * @since   1.2.1
948    */
949   public Set<Stem> getChildStems(Privilege[] privileges, Scope scope)
950     throws  IllegalArgumentException 
951   {
952     this.param.notNullPrivilegeArray(privileges); 
953 
954     Set<Stem> stems = new LinkedHashSet();
955     // TODO 20070824 this could be a lot prettier
956     for ( Stem stem : this.getChildStems(scope) ) {
957 
958       for ( Privilege priv : PrivilegeHelper.getNamingPrivileges(privileges) ) {
959         try {
960           PrivilegeHelper.dispatch( GrouperSession.staticGrouperSession(), stem, 
961               GrouperSession.staticGrouperSession().getSubject(), priv );
962           stems.add(stem);
963           break; // we only care that one privilege matches
964         }
965         catch (InsufficientPrivilegeException eIP) {
966           // ignore
967         }
968         catch (SchemaException eSchema) {
969           // ignore
970         }
971       }
972 
973       if ( !stems.contains(stem) ) { // no matching naming privileges so checking access privilegees
974         // filtering out naming privileges will happen in "#getChildGroups(Privilege[], Scope)"
975         for ( Group group : stem.getChildGroups(privileges, scope) ) {
976           stems.add( group.getParentStem() );
977         }
978       }
979 
980     }
981     return stems;
982   }
983 
984   /**
985    * Get subject that created this stem.
986    * <pre class="eg">
987    * // Get creator of this stem.
988    * try {
989    *   Subject creator = ns.getCreateSubject();
990    * }
991    * catch (SubjectNotFoundException e) {
992    *   // Couldn't find subject
993    * }
994    * </pre>
995    * @return  {@link Subject} that created this stem.
996    * @throws  SubjectNotFoundException
997    */
998   public Subject getCreateSubject() 
999     throws  SubjectNotFoundException
1000   {
1001     if (this.creator == null) {
1002       String subjectId = null;
1003       try {
1004         Member member = MemberFinder.findByUuid( GrouperSession.staticGrouperSession(), 
1005             this.getCreatorUuid(), true );
1006         subjectId = member.getSubjectId();
1007         this.creator = member.getSubject();
1008       }
1009       catch (MemberNotFoundException eMNF) {
1010         throw new SubjectNotFoundException( subjectId, eMNF.getMessage(), eMNF );
1011       }
1012     }
1013     return this.creator; 
1014   } // public Subject getCreateSubject()
1015   
1016   /**
1017    * Get creation time for this stem.
1018    * <pre class="eg">
1019    * // Get create time.
1020    * Date created = ns.getCreateTime();
1021    * </pre>
1022    * @return  {@link Date} that this stem was created.
1023    */
1024   public Date getCreateTime() {
1025     return new Date( this.getCreateTimeLong() );
1026   } // public Date getCreateTime()
1027 
1028   /**
1029    * Get subjects with CREATE privilege on this stem.
1030    * <pre class="eg">
1031    * Set creators = ns.getCreators();
1032    * </pre>
1033    * @return  Set of {@link Subject} objects
1034    * @throws  GrouperException
1035    */
1036   public Set getCreators() 
1037     throws  GrouperException
1038   {
1039     return GrouperSession.staticGrouperSession().getNamingResolver().getSubjectsWithPrivilege(this, NamingPrivilege.CREATE);
1040   }
1041 
1042   /**
1043    * Get stem description.
1044    * <pre class="eg">
1045    * // Get description
1046    * String description = ns.getDescription();
1047    * </pre>
1048    * @return  Stem description.
1049    */
1050   public String getDescription() {
1051     String desc = this.description;
1052     if (desc == null) {
1053       desc = GrouperConfig.EMPTY_STRING;
1054     }
1055     return desc;
1056   } 
1057  
1058   /**
1059    * Get stem displayExtension.
1060    * <pre class="eg">
1061    * // Get displayExtension
1062    * String displayExtn = ns.getDisplayExtension();
1063    * </pre>
1064    * @return  Stem displayExtension.
1065    */
1066   public String getDisplayExtension() {
1067     String val = this.getDisplayExtensionDb();
1068     if (val.equals(ROOT_INT)) {
1069       return ROOT_NAME;
1070     }
1071     return val;
1072   }
1073  
1074   /**
1075    * Get stem displayName.
1076    * <pre class="eg">
1077    * // Get displayName
1078    * String displayName = ns.getDisplayName();
1079    * </pre>
1080    * @return  Stem displayName.
1081    */
1082   public String getDisplayName() {
1083     String val = this.getDisplayNameDb();
1084     if (val.equals(ROOT_INT)) {
1085       return ROOT_NAME;
1086     }
1087     return val;
1088   }
1089  
1090   /**
1091    * Get stem extension.
1092    * <pre class="eg">
1093    * // Get extension
1094    * String extension = ns.getExtension();
1095    * </pre>
1096    * @return  Stem extension.
1097    */
1098   public String getExtension() {
1099     String val = this.getExtensionDb();
1100     if (val.equals(ROOT_INT)) {
1101       return ROOT_NAME;
1102     }
1103     return val;
1104   }
1105  
1106   /**
1107    * Get subject that last modified this stem.
1108    * <pre class="eg">
1109    * // Get last modifier of this stem.
1110    * try {
1111    *   Subject modifier = ns.getModifySubject();
1112    * }
1113    * catch (SubjectNotFoundException e) {
1114    *   // Couldn't find subject
1115    * }
1116    * </pre>
1117    * @return  {@link Subject} that last modified this stem.
1118    * @throws  SubjectNotFoundException
1119    */
1120   public Subject getModifySubject() 
1121     throws  SubjectNotFoundException
1122   {
1123     if (this.modifier == null) {
1124       if ( this.getModifierUuid() == null) {
1125         throw new SubjectNotFoundException("stem has not been modified");
1126       }
1127       try {
1128         this.modifier = MemberFinder.findByUuid( GrouperSession.staticGrouperSession(), 
1129             this.getModifierUuid() , true).getSubject();
1130       }
1131       catch (MemberNotFoundException eMNF) {
1132         throw new SubjectNotFoundException( eMNF.getMessage(), eMNF );
1133       }
1134     }
1135     return this.modifier; 
1136   } // public Subject getModifySubject()
1137   
1138   /**
1139    * Get last modified time for this stem.
1140    * <pre class="eg">
1141    * // Get last modified time.
1142    * Date modified = ns.getModifyTime();
1143    * </pre>
1144    * @return  {@link Date} that this stem was last modified.
1145    */
1146   public Date getModifyTime() {
1147     return new Date( this.getModifyTimeLong() );
1148   } // public Date getModifyTime()
1149 
1150   /**
1151    * Get stem name.
1152    * <pre class="eg">
1153    * // Get name
1154    * String name = ns.getName();
1155    * </pre>
1156    * @return  Stem name.
1157    */ 
1158   public String getName() {
1159     String val = this.getNameDb();
1160     if (StringUtils.equals(ROOT_INT, val)) {
1161       return ROOT_NAME;
1162     }
1163     return val;
1164   }
1165 
1166   /**
1167    * Get parent stem.
1168    * <pre class="eg">
1169    * // Get parent
1170    * Stem parent = ns.getParentStem();
1171    * </pre>
1172    * @return  Parent {@link Stem}.
1173    * @throws StemNotFoundException if stem not found
1174    */
1175   public Stem getParentStem() 
1176     throws StemNotFoundException
1177   {
1178     String uuid = this.getParentUuid();
1179     if (uuid == null) {
1180       throw new StemNotFoundException();
1181     }
1182     Stem parent = GrouperDAOFactory.getFactory().getStem().findByUuid(uuid, true);
1183     return parent;
1184   } // public Stem getParentStem()
1185   
1186   /**
1187    * Get parent stem.
1188    * <pre class="eg">
1189    * // Get parent
1190    * Stem parent = ns.getParentStem();
1191    * </pre>
1192    * @return  Parent {@link Stem}.
1193    * @throws StemNotFoundException if stem not found
1194    */
1195   public Stem getParentStemOrNull() 
1196   {
1197     String theParentUuid = this.getParentUuid();
1198     if (theParentUuid == null) {
1199       return null;
1200     }
1201     Stem parent = GrouperDAOFactory.getFactory().getStem().findByUuid(theParentUuid, true);
1202     return parent;
1203   }
1204   
1205   /**
1206    * Returns the alternate name for the stem.  Used by hibernate.
1207    * @return the alternate name
1208    */
1209   public String getAlternateNameDb() {
1210     return this.alternateNameDb;
1211   }
1212   
1213   /**
1214    * Returns the alternate name for the stem.  If multiple, returns the first one
1215    * @return the alternate name
1216    */
1217   public String getAlternateName() {
1218     return this.alternateNameDb;
1219   }
1220   
1221   /**
1222    * Set the group's alternate name  Used by hibernate.
1223    * @param alternateName
1224    */
1225   public void setAlternateNameDb(String alternateName) {
1226     this.alternateNameDb = alternateName;
1227     this.alternateNames = null;
1228   }
1229   
1230   /** alternate names */
1231   private Set<String> alternateNames = null;
1232   
1233   /**
1234    * Returns the alternate names for the stem.  Only one alternate name is supported
1235    * currently, so a Set of size 0 or 1 will be returned.
1236    * @return Set of alternate names.
1237    */
1238   public Set<String> getAlternateNames() {
1239     
1240     //lazy load this set
1241     if (this.alternateNames == null) {
1242       this.alternateNames = new LinkedHashSet<String>();
1243       if (!StringUtils.isBlank(this.alternateNameDb)) {
1244         this.alternateNames.add(this.alternateNameDb);
1245       }
1246       this.alternateNames = Collections.unmodifiableSet(this.alternateNames);
1247     }
1248     return this.alternateNames;
1249   }
1250 
1251   /**
1252    * Add an alternate name for this stem.  Only one alternate name is supported
1253    * currently, so this will replace any existing alternate name.
1254    * This won't get saved until you call store().
1255    * @param alternateName
1256    */
1257   public void addAlternateName(String alternateName) {
1258     
1259     if (!PrivilegeHelper.canStemAdmin(this, GrouperSession.staticGrouperSession().getSubject())) {
1260       throw new InsufficientPrivilegeException(E.CANNOT_STEM_ADMIN);
1261     }
1262     
1263     // verify that if the property security.stem.groupAllowedToRenameStem is set,
1264     // then the user is a member of that group.
1265     if (!PrivilegeHelper.canRenameStems(GrouperSession.staticGrouperSession().getSubject())) {
1266       throw new InsufficientPrivilegeException("User cannot rename stems.");      
1267     }
1268     
1269     // verify name
1270     GrouperValidator v = AddAlternateStemNameValidator.validate(alternateName);
1271     if (v.isInvalid()) {
1272       throw new StemModifyException(v.getErrorMessage() + ": " + alternateName);
1273     }
1274     
1275     // Checking stem privilege on the parent stem if the alternate name is for another stem
1276     String parentStemName = GrouperUtil.parentStemNameFromName(alternateName);
1277     if (GrouperUtil.isEmpty(parentStemName)) {
1278       parentStemName = Stem.ROOT_NAME;
1279     }
1280     
1281     if (this.isRootStem() || !parentStemName.equals(this.getParentStem().getName())) {
1282       Stem stem = GrouperUtil.getFirstParentStemOfName(alternateName);
1283 
1284       if (!stem.hasStem(GrouperSession.staticGrouperSession().getSubject())) {
1285         throw new InsufficientPrivilegeException(E.CANNOT_STEM);
1286       }
1287     }
1288     
1289     internal_addAlternateName(alternateName);
1290   }
1291   
1292   /**
1293    * Add an alternate name for this stem.  Only one alternate name is supported
1294    * currently, so this will replace any existing alternate name.
1295    * This won't get saved until you call store().
1296    * @param alternateName
1297    */
1298   protected void internal_addAlternateName(String alternateName) {
1299 
1300     this.alternateNameDb = alternateName;
1301   }
1302   
1303   /**
1304    * Delete the specified alternate name.  This won't get saved until you call store().
1305    * @param alternateName
1306    * @return false if the stem does not have the specified alternate name
1307    */
1308   public boolean deleteAlternateName(String alternateName) {
1309     
1310     if (!PrivilegeHelper.canStemAdmin(this, GrouperSession.staticGrouperSession().getSubject())) {
1311       throw new InsufficientPrivilegeException(E.CANNOT_STEM_ADMIN);
1312     }
1313     
1314     // verify that if the property security.stem.groupAllowedToRenameStem is set,
1315     // then the user is a member of that group.
1316     if (!PrivilegeHelper.canRenameStems(GrouperSession.staticGrouperSession().getSubject())) {
1317       throw new InsufficientPrivilegeException("User cannot rename stems.");      
1318     }
1319     
1320     if (alternateName.equals(this.alternateNameDb)) {
1321       this.alternateNameDb = null;
1322       return true;
1323     }
1324     
1325     return false;
1326   }
1327 
1328   /**
1329    * Get privileges that the specified subject has on this stem.
1330    * <pre class="eg">
1331    * Set privs = ns.getPrivs(subj);
1332    * </pre>
1333    * @param   subj  Get privileges for this subject.
1334    * @return  Set of {@link NamingPrivilege} objects.
1335    */
1336   public Set<NamingPrivilege> getPrivs(Subject subj) {
1337     return GrouperSession.staticGrouperSession().getNamingResolver().getPrivileges(this, subj);
1338   } 
1339 
1340   /**
1341    * Get subjects with STEM privilege on this stem.
1342    * <pre class="eg">
1343    * Set stemmers = ns.getStemmers();
1344    * </pre>
1345    * @return  Set of {@link Subject} objects
1346    * @throws  GrouperException
1347    */
1348   public Set getStemmers() 
1349     throws  GrouperException
1350   {
1351     return getStemAdmins();
1352   } 
1353   
1354   /**
1355    * Get subjects with STEM_ADMIN privilege on this stem.
1356    * <pre class="eg">
1357    * Set stemAdmins = ns.getStemAdmins();
1358    * </pre>
1359    * @return  Set of {@link Subject} objects
1360    * @throws  GrouperException
1361    */
1362   public Set getStemAdmins() 
1363     throws  GrouperException
1364   {
1365     return GrouperSession.staticGrouperSession().getNamingResolver().getSubjectsWithPrivilege(this, NamingPrivilege.STEM_ADMIN);
1366   } 
1367   
1368   /**
1369    * Get subjects with STEM_ATTR_READ privilege on this stem.
1370    * <pre class="eg">
1371    * Set subjects = ns.getStemAttrReaders();
1372    * </pre>
1373    * @return  Set of {@link Subject} objects
1374    * @throws  GrouperException
1375    */
1376   public Set getStemAttrReaders() 
1377     throws  GrouperException
1378   {
1379     return GrouperSession.staticGrouperSession().getNamingResolver().getSubjectsWithPrivilege(this, NamingPrivilege.STEM_ATTR_READ);
1380   } 
1381   
1382   /**
1383    * Get subjects with STEM_ATTR_UPDATE privilege on this stem.
1384    * <pre class="eg">
1385    * Set subjects = ns.getStemAttrUpdaters();
1386    * </pre>
1387    * @return  Set of {@link Subject} objects
1388    * @throws  GrouperException
1389    */
1390   public Set getStemAttrUpdaters() 
1391     throws  GrouperException
1392   {
1393     return GrouperSession.staticGrouperSession().getNamingResolver().getSubjectsWithPrivilege(this, NamingPrivilege.STEM_ATTR_UPDATE);
1394   } 
1395 
1396   /**
1397    * @return uuid
1398    */
1399   public String getUuid() {
1400     return this.uuid;
1401   } // public String getUuid()
1402 
1403   /**
1404    * Grant a privilege on this stem.
1405    * <pre class="eg">
1406    * try {
1407    *   ns.grantPriv(subj, NamingPrivilege.CREATE);
1408    * }
1409    * catch (GrantPrivilegeException e) {
1410    *   // Error granting privilege
1411    * }
1412    * </pre>
1413    * @param   subj  Grant privilege to this subject.
1414    * @param   priv  Grant this privilege.
1415    * @throws  GrantPrivilegeException
1416    * @throws  InsufficientPrivilegeException
1417    * @throws  SchemaException
1418    */
1419   public void grantPriv(Subject subj, Privilege priv)
1420     throws  GrantPrivilegeException,        // TODO 20070820 stop throwing
1421             InsufficientPrivilegeException, // TODO 20070820 stop throwing
1422             SchemaException                 // TODO 20070820 stop throwing
1423   {
1424     grantPriv(subj, priv, true);
1425     
1426   }
1427   
1428   /**
1429    * Grant a privilege on this stem.
1430    * <pre class="eg">
1431    * try {
1432    *   ns.grantPriv(subj, NamingPrivilege.CREATE);
1433    * }
1434    * catch (GrantPrivilegeException e) {
1435    *   // Error granting privilege
1436    * }
1437    * </pre>
1438    * @param   subj  Grant privilege to this subject.
1439    * @param   priv  Grant this privilege.
1440    * @param exceptionIfAlreadyMember if false, and subject is already a member,
1441    * then dont throw a MemberAddException if the member is already in the group
1442    * @throws  GrantPrivilegeException
1443    * @throws  InsufficientPrivilegeException
1444    * @throws  SchemaException
1445    * @return false if it already existed, true if it didnt already exist
1446    */
1447   public boolean grantPriv(final Subject subj, final Privilege priv, final boolean exceptionIfAlreadyMember) 
1448     throws  GrantPrivilegeException,        
1449             InsufficientPrivilegeException, 
1450             SchemaException {
1451     return internal_grantPriv(subj, priv, exceptionIfAlreadyMember, null);
1452   }
1453   
1454   /**
1455    * Grant a privilege on this stem.
1456    * <pre class="eg">
1457    * try {
1458    *   ns.grantPriv(subj, NamingPrivilege.CREATE);
1459    * }
1460    * catch (GrantPrivilegeException e) {
1461    *   // Error granting privilege
1462    * }
1463    * </pre>
1464    * @param   subj  Grant privilege to this subject.
1465    * @param   priv  Grant this privilege.
1466    * @param exceptionIfAlreadyMember if false, and subject is already a member,
1467    * then dont throw a MemberAddException if the member is already in the group
1468    * @param uuid
1469    * @throws  GrantPrivilegeException
1470    * @throws  InsufficientPrivilegeException
1471    * @throws  SchemaException
1472    * @return false if it already existed, true if it didnt already exist
1473    */
1474   public boolean internal_grantPriv(final Subject subj, final Privilege priv, final boolean exceptionIfAlreadyMember, final String uuid) 
1475     throws  GrantPrivilegeException,        
1476             InsufficientPrivilegeException, 
1477             SchemaException {
1478     final StopWatch sw = new StopWatch();
1479     sw.start();
1480     
1481     final String errorMessageSuffix = ", stem name: " + this.name 
1482       + ", subject: " + GrouperUtil.subjectToString(subj) + ", privilege: " + (priv == null ? null : priv.getName());
1483 
1484     return (Boolean)HibernateSession.callbackHibernateSession(
1485       GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
1486       new HibernateHandler() {
1487   
1488         public Object callback(HibernateHandlerBean hibernateHandlerBean)
1489             throws GrouperDAOException {
1490 
1491     
1492           boolean didNotExist = true;
1493           try {
1494 
1495             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
1496 
1497             GrouperSession.staticGrouperSession().getNamingResolver().grantPrivilege(Stem.this, subj, priv, uuid);
1498             
1499             RulesPrivilegeBeanivilegeBean.html#RulesPrivilegeBean">RulesPrivilegeBean rulesPrivilegeBean = new RulesPrivilegeBean(Stem.this, subj, priv);
1500             
1501             //fire rules related to subject assign in folder
1502             RuleEngine.fireRule(RuleCheckType.subjectAssignInStem, rulesPrivilegeBean);
1503             
1504             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
1505               
1506               Member member = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subj, false);
1507               
1508               AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.PRIVILEGE_STEM_ADD, "privilegeName", 
1509                   priv.getName(),  "memberId",  member.getUuid(),
1510                       "privilegeType", "naming", 
1511                       "stemId", Stem.this.getUuid(), "stemName", Stem.this.getName());
1512                       
1513               auditEntry.setDescription("Added privilege: stem: " + Stem.this.getName()
1514                   + ", subject: " + subj.getSource().getId() + "." + subj.getId() + ", privilege: "
1515                   + priv.getName());
1516               auditEntry.saveOrUpdate(true);
1517             }
1518 
1519             
1520           } catch (UnableToPerformAlreadyExistsException eUTP) {
1521             if (exceptionIfAlreadyMember) {
1522               throw new GrantPrivilegeAlreadyExistsException( eUTP.getMessage() + errorMessageSuffix, eUTP );
1523             }
1524             didNotExist = false;
1525           }
1526           catch (UnableToPerformException eUTP) {
1527             throw new GrantPrivilegeException( eUTP.getMessage() + errorMessageSuffix, eUTP );
1528           }
1529           sw.stop();
1530           if (didNotExist) {
1531             EL.stemGrantPriv(GrouperSession.staticGrouperSession(), Stem.this.getName(), subj, priv, sw);
1532           }
1533           return didNotExist;
1534         }
1535       });
1536   } 
1537 
1538   /**
1539    * Check whether a subject has the CREATE privilege on this stem.
1540    * <pre class="eg">
1541    * if (ns.hasCreate(subj)) {
1542    *   // Has CREATE
1543    * }
1544    *   // Does not have CREATE
1545    * } 
1546    * </pre>
1547    * @param   subj  Check whether this subject has CREATE.
1548    * @return  Boolean true if the subject has CREATE.
1549    */
1550   public boolean hasCreate(Subject subj) {
1551     return GrouperSession.staticGrouperSession().getNamingResolver().hasPrivilege(this, subj, NamingPrivilege.CREATE);
1552   } 
1553   
1554   /**
1555    * Check whether a subject has the STEM_ATTR_READ privilege on this stem.
1556    * <pre class="eg">
1557    * if (ns.hasStemAttrRead(subj)) {
1558    *   // Has STEM_ATTR_READ
1559    * }
1560    *   // Does not have STEM_ATTR_READ
1561    * } 
1562    * </pre>
1563    * @param   subj  Check whether this subject has STEM_ATTR_READ.
1564    * @return  Boolean true if the subject has STEM_ATTR_READ.
1565    */
1566   public boolean hasStemAttrRead(Subject subj) {
1567     return GrouperSession.staticGrouperSession().getNamingResolver().hasPrivilege(this, subj, NamingPrivilege.STEM_ATTR_READ);
1568   }
1569   
1570   /**
1571    * Check whether a subject has the STEM_ATTR_UPDATE privilege on this stem.
1572    * <pre class="eg">
1573    * if (ns.hasStemAttrUpdate(subj)) {
1574    *   // Has STEM_ATTR_UPDATE
1575    * }
1576    *   // Does not have STEM_ATTR_UPDATE
1577    * } 
1578    * </pre>
1579    * @param   subj  Check whether this subject has STEM_ATTR_UPDATE.
1580    * @return  Boolean true if the subject has STEM_ATTR_UPDATE.
1581    */
1582   public boolean hasStemAttrUpdate(Subject subj) {
1583     return GrouperSession.staticGrouperSession().getNamingResolver().hasPrivilege(this, subj, NamingPrivilege.STEM_ATTR_UPDATE);
1584   }
1585  
1586   /**
1587    * Check whether a member has the STEM privilege on this stem.
1588    * <pre class="eg">
1589    * if (ns.hasStem(subj)) {
1590    *   // Has STEM
1591    * }
1592    *   // Does not have STEM
1593    * } 
1594    * </pre>
1595    * @param   subj  check whether this subject has STEM.
1596    * @return  Boolean true if the subject has STEM.
1597    */
1598   public boolean hasStem(Subject subj) {
1599     return hasStemAdmin(subj);
1600   } 
1601   
1602   /**
1603    * Check whether a member has the STEM_ADMIN privilege on this stem.
1604    * <pre class="eg">
1605    * if (ns.hasStemAdmin(subj)) {
1606    *   // Has STEM_ADMIN
1607    * }
1608    *   // Does not have STEM_ADMIN
1609    * } 
1610    * </pre>
1611    * @param   subj  check whether this subject has STEM_ADMIN.
1612    * @return  Boolean true if the subject has STEM_ADMIN.
1613    */
1614   public boolean hasStemAdmin(Subject subj) {
1615     return GrouperSession.staticGrouperSession().getNamingResolver().hasPrivilege(this, subj, NamingPrivilege.STEM_ADMIN);
1616   } 
1617  
1618   /**
1619    * see if the subject has a privilege
1620    * @param subject
1621    * @param privilegeOrListName
1622    * @return true if has privilege
1623    */
1624   public boolean hasPrivilege(Subject subject, String privilegeOrListName) {
1625     if (StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM.getName()) 
1626         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM.getListName())
1627         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ADMIN.getName()) 
1628         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ADMIN.getListName())) {
1629       return this.hasStemAdmin(subject);
1630     }
1631     if (StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.CREATE.getName()) 
1632         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.CREATE.getListName())) {
1633       return this.hasCreate(subject);
1634     }
1635     if (StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ATTR_READ.getName()) 
1636         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ATTR_READ.getListName())) {
1637       return this.hasStemAttrRead(subject);
1638     }
1639     if (StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ATTR_UPDATE.getName()) 
1640         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ATTR_UPDATE.getListName())) {
1641       return this.hasStemAttrUpdate(subject);
1642     }
1643     throw new RuntimeException("Cant find privilege: '" + privilegeOrListName + "'");
1644 
1645   }
1646 
1647   /**
1648    * TODO 20070813 make public?
1649    * @param group group
1650    * @return  True if <i>group</i> is child, at any depth, of this stem.
1651    * @throws  IllegalArgumentException if <i>group</i> is null.
1652    * @since   1.2.1
1653    */
1654   public boolean isChildGroup(Group group)
1655     throws  IllegalArgumentException
1656   {
1657     if (group == null) { // TODO 20070813 ParameterHelper
1658       throw new IllegalArgumentException("null Group");
1659     }
1660 
1661     if (this.isRootStem()) {
1662       return true;
1663     } 
1664 
1665     String stemName = this.getName();
1666     String groupName = group.getName();
1667 
1668     if (groupName.length() <= (stemName.length() + DELIM.length())) {
1669       return false;
1670     }
1671     
1672     if ((stemName + DELIM).equals(groupName.substring(0, stemName.length() + DELIM.length()))) {
1673       return true;
1674     }
1675 
1676     return false;
1677   }
1678 
1679   /**
1680    * TODO 20070813 make public?
1681    * @param stem stem
1682    * @return  True if <i>stem</i> is child, at any depth, of this stem.
1683    * @throws  IllegalArgumentException if <i>stem</i> is null.
1684    * @since   1.2.1
1685    */
1686   public boolean isChildStem(Stem stem) 
1687     throws  IllegalArgumentException
1688   {
1689     if (stem == null) { // TODO 20070813 ParameterHelper
1690       throw new IllegalArgumentException("null Stem");
1691     }
1692 
1693     String thisName = this.getName();
1694     String stemName = stem.getName();
1695 
1696     if (
1697          ( thisName.equals( stemName ) )  // can't be child of self
1698          ||
1699          stem.isRootStem()                            // root stem can't be child
1700        )
1701     {
1702       return false;
1703     }
1704     if ( this.isRootStem() ) {
1705       return true; // all stems are children
1706     }
1707 
1708     if (stemName.length() <= (thisName.length() + DELIM.length())) {
1709       return false;
1710     }
1711     
1712     if ((thisName + DELIM).equals(stemName.substring(0, thisName.length() + DELIM.length()))) {
1713       return true;
1714     }
1715 
1716     return false;
1717   }
1718 
1719   /**
1720    * @return  Boolean true if this is the root stem of the Groups Registry.
1721    * @since   1.2.0
1722    */
1723   public boolean isRootStem() {
1724     return ROOT_INT.equals( this.getNameDb() );
1725   } 
1726 
1727   /**
1728    * Revoke all privileges of the specified type on this stem.
1729    * <pre class="eg">
1730    * try {
1731    *   ns.revokePriv(NamingPrivilege.CREATE);
1732    * }
1733    * catch (InsufficientPrivilegeException eIP) {
1734    *   // Not privileged to revoke this privilege
1735    * }
1736    * catch (RevokePrivilegeException eRP) {
1737    *   // Error revoking privilege
1738    * }
1739    * </pre>
1740    * @param   priv  Revoke this privilege.
1741    * @throws  InsufficientPrivilegeException
1742    * @throws  RevokePrivilegeException
1743    * @throws  SchemaException
1744    */
1745   public void revokePriv(Privilege priv) 
1746     throws  InsufficientPrivilegeException, // TODO 20070820 stop throwing this
1747             RevokePrivilegeException,
1748             SchemaException                 // TODO 20070820 stop throwing this
1749   {
1750     StopWatch sw = new StopWatch();
1751     sw.start();
1752     if ( !Privilege.isNaming(priv) ) {
1753       throw new SchemaException("attempt to use not naming privilege");
1754     }
1755     try {
1756       GrouperSession.staticGrouperSession().getNamingResolver().revokePrivilege(this, priv);
1757     }
1758     catch (UnableToPerformException e) {
1759       throw new RevokePrivilegeException( e.getMessage(), e );
1760     }
1761     sw.stop();
1762     EL.stemRevokePriv(GrouperSession.staticGrouperSession(), this.getName(), priv, sw);
1763   }
1764  
1765   /**
1766    * Revoke a privilege on this stem.
1767    * <pre class="eg">
1768    * try {
1769    *   ns.revokePriv(subj, NamingPrivilege.CREATE);
1770    * }
1771    * catch (InsufficientPrivilegeException eIP) {
1772    *   // Not privileged to revoke this privilege
1773    * }
1774    * catch (RevokePrivilegeException eRP) {
1775    *   // Error revoking privilege
1776    * }
1777    * </pre>
1778    * @param   subj  Revoke privilege from this subject.
1779    * @param   priv  Revoke this privilege.
1780    * @throws  InsufficientPrivilegeException
1781    * @throws  RevokePrivilegeException
1782    * @throws  SchemaException
1783    */
1784   public void revokePriv(Subject subj, Privilege priv)
1785     throws  InsufficientPrivilegeException, // TODO 20070820 stop throwing this
1786             RevokePrivilegeException,
1787             SchemaException                 // TODO 20070820 stop throwing this
1788   {
1789     revokePriv(subj, priv, true);
1790   }
1791 
1792   /**
1793    * Revoke a privilege on this stem.
1794    * <pre class="eg">
1795    * try {
1796    *   ns.revokePriv(subj, NamingPrivilege.CREATE);
1797    * }
1798    * catch (InsufficientPrivilegeException eIP) {
1799    *   // Not privileged to revoke this privilege
1800    * }
1801    * catch (RevokePrivilegeException eRP) {
1802    *   // Error revoking privilege
1803    * }
1804    * </pre>
1805    * @param   subj  Revoke privilege from this subject.
1806    * @param   priv  Revoke this privilege.
1807    * @param exceptionIfAlreadyRevoked if false, and subject is already a member,
1808    * then dont throw a MemberAddException if the member is already in the group
1809    * @return false if it was already revoked, true if it wasnt already deleted
1810    * @throws  InsufficientPrivilegeException
1811    * @throws  RevokePrivilegeException
1812    * @throws  SchemaException
1813    */
1814   public boolean revokePriv(final Subject subj, final Privilege priv, final boolean exceptionIfAlreadyRevoked)
1815     throws  InsufficientPrivilegeException,
1816             RevokePrivilegeException,
1817             SchemaException {
1818 
1819     final String errorMessageSuffix = ", stem name: " + this.name 
1820       + ", subject: " + GrouperUtil.subjectToString(subj) + ", privilege: " + (priv == null ? null : priv.getName());
1821 
1822     return (Boolean)HibernateSession.callbackHibernateSession(
1823       GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
1824       new HibernateHandler() {
1825   
1826         public Object callback(HibernateHandlerBean hibernateHandlerBean)
1827             throws GrouperDAOException {
1828 
1829           hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
1830 
1831           boolean wasntAlreadyRevoked = true;
1832           StopWatch sw = new StopWatch();
1833           sw.start();
1834           if (!Privilege.isNaming(priv) ) {
1835             throw new SchemaException("attempt to use non-naming privilege: " + errorMessageSuffix);
1836           }
1837           try {
1838             GrouperSession.staticGrouperSession().getNamingResolver().revokePrivilege(Stem.this, subj, priv);
1839             
1840             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
1841               
1842               Member member = MemberFinder.findBySubject(GrouperSession.staticGrouperSession(), subj, false);
1843               
1844               AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.PRIVILEGE_STEM_DELETE, "privilegeName", 
1845                   priv.getName(),  "memberId",  member.getUuid(),
1846                       "privilegeType", "naming", 
1847                       "stemId", Stem.this.getUuid(), "stemName", Stem.this.getName());
1848                       
1849               auditEntry.setDescription("Deleted privilege: stem: " + Stem.this.getName()
1850                   + ", subject: " + subj.getSource().getId() + "." + subj.getId() + ", privilege: "
1851                   + priv.getName());
1852               auditEntry.saveOrUpdate(true);
1853             }
1854 
1855             
1856           } catch (UnableToPerformAlreadyExistsException eUTP) {
1857             if (exceptionIfAlreadyRevoked) {
1858               throw new RevokePrivilegeAlreadyRevokedException( eUTP.getMessage() + errorMessageSuffix, eUTP );
1859             }
1860             wasntAlreadyRevoked = false;
1861           } catch (UnableToPerformException e) {
1862             throw new RevokePrivilegeException( e.getMessage() + errorMessageSuffix, e );
1863           }
1864           sw.stop();
1865           if (wasntAlreadyRevoked) {
1866             EL.stemRevokePriv(GrouperSession.staticGrouperSession(), Stem.this.getName(), subj, priv, sw);
1867           }
1868           return wasntAlreadyRevoked;
1869         }
1870       });
1871   } 
1872 
1873   /**
1874    * Set stem description.
1875    * <pre class="eg">
1876    * // Set description
1877    * try {
1878    *  ns.setDescription(value);
1879    * }
1880    * }
1881    * catch (InsufficientPrivilegeException e0) {
1882    *   // Not privileged to set description
1883    * catch (StemModifyException e1) {
1884    *   // Error setting description
1885    * }
1886    * </pre>
1887    * @param   value   Set description to this value.
1888    * @throws  InsufficientPrivilegeException
1889    * @throws  StemModifyException
1890    */
1891   public void setDescription(String value) 
1892     throws  InsufficientPrivilegeException,
1893             StemModifyException
1894   {
1895     StopWatch sw = new StopWatch();
1896     sw.start();
1897     if ( !PrivilegeHelper.canStemAdmin( this, GrouperSession.staticGrouperSession().getSubject() ) ) {
1898       throw new InsufficientPrivilegeException(E.CANNOT_STEM_ADMIN);
1899     }
1900     try {
1901       this.setDescriptionDb(value);
1902       this.internal_setModified();
1903       sw.stop();
1904       EL.stemSetAttr(GrouperSession.staticGrouperSession(), this.getName(), "description", value, sw);
1905       
1906     }
1907     catch (GrouperDAOException eDAO) {
1908       throw new StemModifyException( "unable to set description: " + eDAO.getMessage(), eDAO );
1909     }
1910   }
1911 
1912   /**
1913    * will be implemented soon
1914    */
1915   public void store() {
1916 
1917     validate();
1918 
1919     HibernateSession.callbackHibernateSession(
1920         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
1921         new HibernateHandler() {
1922 
1923           public Object callback(HibernateHandlerBean hibernateHandlerBean)
1924               throws GrouperDAOException {
1925 
1926             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
1927 
1928             String differences = GrouperUtil.dbVersionDescribeDifferences(Stem.this.dbVersion(), 
1929                 Stem.this, Stem.this.dbVersion() != null ? Stem.this.dbVersionDifferentFields() : Stem.CLONE_FIELDS);
1930 
1931             try {
1932               GrouperDAOFactory.getFactory().getStem().update( Stem.this );
1933             }
1934             catch (GrouperDAOException e) {
1935               String error = "Problem with hib update: " + GrouperUtil.toStringSafe(Stem.this)
1936                + ",\n" + e.getMessage();
1937               GrouperUtil.injectInException(e, error);
1938               throw e;
1939             }
1940           
1941             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
1942               AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.STEM_UPDATE, "id", 
1943                   Stem.this.getUuid(), "name", Stem.this.getName(), "parentStemId", Stem.this.getParentUuid(), "displayName", 
1944                   Stem.this.getDisplayName(), "description", Stem.this.getDescription());
1945               auditEntry.setDescription("Updated stem: " + Stem.this.getName() + ", " + differences);
1946               auditEntry.saveOrUpdate(true);
1947             }
1948             return null;
1949           }
1950         });
1951           
1952 
1953     
1954   }
1955 
1956   /**
1957    * 
1958    */
1959   public void validate() {
1960     //lets validate
1961     
1962     int maxNameLength = 255;
1963     maxNameLength = GrouperConfig.retrieveConfig().propertyValueInt("grouper.stemName.maxSize", maxNameLength);
1964 
1965     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(stemsTable, "extension", 
1966     //        Types.VARCHAR, "255", false, true);
1967     if (GrouperUtil.lengthAscii(this.getExtension()) > 255 ) {
1968       throw new GrouperValidationException("Stem extension too long: " + GrouperUtil.lengthAscii(this.getExtension()), 
1969           VALIDATION_STEM_EXTENSION_TOO_LONG_KEY, 255, GrouperUtil.lengthAscii(this.getExtension()));
1970     }
1971 
1972     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(stemsTable, "display_extension", 
1973     //        Types.VARCHAR, "255", false, true);
1974     if (GrouperUtil.lengthAscii(this.getDisplayExtension()) > 255 ) {
1975       throw new GrouperValidationException("Stem display extension too long: " + GrouperUtil.lengthAscii(this.getDisplayExtension()), 
1976           VALIDATION_STEM_DISPLAY_EXTENSION_TOO_LONG_KEY, 255, GrouperUtil.lengthAscii(this.getDisplayName()));
1977     }
1978 
1979     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(stemsTable, "name", 
1980     //        Types.VARCHAR, "255", false, true);
1981     if (GrouperUtil.lengthAscii(this.getName()) > maxNameLength) {
1982       throw new GrouperValidationException("Stem name too long: " + GrouperUtil.lengthAscii(this.getName()), 
1983           VALIDATION_STEM_NAME_TOO_LONG_KEY, 255, GrouperUtil.lengthAscii(this.getName()));
1984     }
1985 
1986     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(stemsTable, "display_name", 
1987     //        Types.VARCHAR, "255", false, true);
1988     if (GrouperUtil.lengthAscii(this.getDisplayName()) > maxNameLength) {
1989       throw new GrouperValidationException("Stem display name too long: " + GrouperUtil.lengthAscii(this.getDisplayName()), 
1990           VALIDATION_STEM_DISPLAY_NAME_TOO_LONG_KEY, 255, GrouperUtil.lengthAscii(this.getDisplayName()));
1991     }
1992 
1993     //    GrouperDdlUtils.ddlutilsFindOrCreateColumn(stemsTable, "description", 
1994     //        Types.VARCHAR, "1024", false, false);
1995     if (GrouperUtil.lengthAscii(this.getDescription()) > 1024 ) {
1996       throw new GrouperValidationException("Stem description too long: " + GrouperUtil.lengthAscii(this.getDescription()), 
1997           VALIDATION_STEM_DESCRIPTION_TOO_LONG_KEY, 1024, GrouperUtil.lengthAscii(this.getDescription()));
1998     }
1999   }
2000   
2001   /**
2002    * Set <i>displayExtension</i>.
2003    * <p>This will also update the <i>displayName</i> of all child stems and groups.</p>
2004    * <pre class="eg">
2005    * try {
2006    *  ns.setDisplayExtension(value);
2007    * }
2008    * catch (InsufficientPrivilegeException eIP) {
2009    *   // Not privileged to set displayExtension
2010    * catch (StemModifyException eNSM) {
2011    *   // Error setting displayExtension
2012    * }
2013    * </pre>
2014    * @param   value   Set displayExtension to this value.
2015    * @throws  InsufficientPrivilegeException
2016    * @throws  StemModifyException
2017    */
2018   public void setDisplayExtension(String value) 
2019     throws  InsufficientPrivilegeException,
2020             StemModifyException
2021   {
2022     // If root stem, give exception.  I'm leaving the root stem specific logic below
2023     // in case we want to remove this later.  But if we remove this, the onPreUpdate logic 
2024     // for name changes would need to be adjusted.
2025     if (this.isRootStem()) {
2026       throw new StemModifyException("cannot set display extension on root stem.");
2027     }
2028     
2029     StopWatch sw = new StopWatch();
2030     sw.start();
2031     NamingValidator nv = NamingValidator.validate(value);
2032     if (nv.isInvalid()) {
2033       if ( this.isRootStem() && value.equals(ROOT_NAME) ) {
2034         // Appease Oracle
2035         value = ROOT_INT;   
2036       }
2037       else {
2038         throw new StemModifyException( nv.getErrorMessage() );
2039       }
2040     }
2041     if ( !PrivilegeHelper.canStemAdmin( this, GrouperSession.staticGrouperSession().getSubject() ) ) {
2042       throw new InsufficientPrivilegeException(E.CANNOT_STEM_ADMIN);
2043     }
2044     try {
2045       this.setDisplayExtensionDb(value);
2046       this.internal_setModified();
2047       if (this.isRootStem()) {
2048         this.setDisplayNameDb(value);
2049       }
2050       else {
2051         try {
2052           this.setDisplayNameDb( U.constructName( this.getParentStem().getDisplayName(), value ) );
2053         }
2054         catch (StemNotFoundException eShouldNeverHappen) {
2055           throw new IllegalStateException( 
2056             "this should never happen: non-root stem without parent: " + eShouldNeverHappen.getMessage(), eShouldNeverHappen 
2057           );
2058         }
2059       }
2060     }
2061     catch (GrouperDAOException eDAO) {
2062       throw new StemModifyException( "unable to set displayExtension: " + eDAO.getMessage(), eDAO );
2063     }
2064     sw.stop();
2065     // Reset for logging purposes
2066     if (value.equals(ROOT_INT)) {
2067       value = ROOT_NAME;
2068     }
2069     EL.stemSetAttr(GrouperSession.staticGrouperSession(), this.getName(), "displayExtension", value, sw);
2070   } // public void setDisplayExtension(value)
2071 
2072   
2073   /**
2074    * Set <i>extension</i>.
2075    * <p>This will also update the <i>name</i> of all child stems and groups.</p>
2076    * <pre class="eg">
2077    * try {
2078    *  ns.setExtension(value);
2079    * }
2080    * catch (InsufficientPrivilegeException eIP) {
2081    *   // Not privileged to set "extension"
2082    * catch (StemModifyException eNSM) {
2083    *   // Error setting "extension"
2084    * }
2085    * </pre>
2086    * @param   value   Set <i>extension</i> to this value.
2087    * @throws  InsufficientPrivilegeException
2088    * @throws  StemModifyException
2089    */
2090   public void setExtension(String value) 
2091     throws  InsufficientPrivilegeException,
2092             StemModifyException {
2093      setExtension(value, true);
2094   }
2095   
2096   /**
2097    * Set <i>extension</i>.
2098    * <p>This will also update the <i>name</i> of all child stems and groups.</p>
2099    * <pre class="eg">
2100    * try {
2101    *  ns.setExtension(value, true);
2102    * }
2103    * catch (InsufficientPrivilegeException eIP) {
2104    *   // Not privileged to set "extension"
2105    * catch (StemModifyException eNSM) {
2106    *   // Error setting "extension"
2107    * }
2108    * </pre>
2109    * @param   value   Set <i>extension</i> to this value.
2110    * @param   assignAlternateName   Whether to add the old group and stem names as 
2111    *                                alternate names for any renamed groups and stems.
2112    * @throws  InsufficientPrivilegeException
2113    * @throws  StemModifyException
2114    */
2115   public void setExtension(String value, boolean assignAlternateName) 
2116     throws  InsufficientPrivilegeException,
2117             StemModifyException
2118   {
2119     // If root stem, give exception.  I'm leaving the root stem specific logic below
2120     // in case we want to remove this later.  But if we remove this, we'll have to deal
2121     // with parts of the code that identify the root stem as "" or ":".
2122     // Also, the onPreUpdate logic for name changes would need to be adjusted.
2123     if (this.isRootStem()) {
2124       throw new StemModifyException("cannot set extension on root stem.");
2125     }
2126     
2127     // TODO 20070531 DRY w/ "setDisplayExtension"
2128     StopWatch sw = new StopWatch();
2129     sw.start();
2130     NamingValidator nv = NamingValidator.validate(value);
2131     if (nv.isInvalid()) {
2132       if ( this.isRootStem() && value.equals(ROOT_NAME) ) {
2133         // Appease Oracle
2134         value = ROOT_INT;   
2135       }
2136       else {
2137         throw new StemModifyException( nv.getErrorMessage() );
2138       }
2139     }
2140     if ( !PrivilegeHelper.canStemAdmin( this, GrouperSession.staticGrouperSession().getSubject() ) ) {
2141       throw new InsufficientPrivilegeException(E.CANNOT_STEM_ADMIN);
2142     }
2143     
2144     // verify that if the property security.stem.groupAllowedToRenameStem is set,
2145     // then the user is a member of that group.
2146     if (!PrivilegeHelper.canRenameStems(GrouperSession.staticGrouperSession().getSubject())) {
2147       throw new InsufficientPrivilegeException("User cannot rename stems.");      
2148     }
2149     
2150     String oldExtension = null;
2151     if (this.dbVersion() != null) {
2152       oldExtension = this.dbVersion().getExtensionDb();
2153     }
2154     
2155     if (assignAlternateName && oldExtension != null && !oldExtension.equals(value)) {
2156       internal_addAlternateName(this.dbVersion().getNameDb());
2157     }
2158     
2159     try {
2160       this.setExtensionDb(value);
2161       this.internal_setModified();
2162       if (this.isRootStem()) {
2163         this.setNameDb(value);
2164       }
2165       else {
2166         try {
2167           this.setNameDb( U.constructName( this.getParentStem().getName(), value ) );
2168         }
2169         catch (StemNotFoundException eShouldNeverHappen) {
2170           throw new IllegalStateException( 
2171             "this should never happen: non-root stem without parent: " + eShouldNeverHappen.getMessage(), eShouldNeverHappen 
2172           );
2173         }
2174       }
2175     }
2176     catch (GrouperDAOException eDAO) {
2177       throw new StemModifyException( "unable to set extension: " + eDAO.getMessage(), eDAO );
2178     }
2179     sw.stop();
2180     // Reset for logging purposes
2181     if (value.equals(ROOT_INT)) {
2182       value = ROOT_NAME;
2183     }
2184     EL.stemSetAttr( GrouperSession.staticGrouperSession(), this.getName(), "extension", value, sw );
2185     
2186     this.setAlternateNameOnMovesAndRenames = assignAlternateName;
2187   } // public void setExtension(value)
2188 
2189   /**
2190    * 
2191    * @see java.lang.Object#toString()
2192    */
2193   public String toString() {
2194     return new ToStringBuilder(this)
2195       .append( "displayName", this.getDisplayName()  )
2196       .append( "name",  this.getName()         )
2197       .append( "uuid",                this.getUuid()         )
2198       .append( "creator",             this.getCreatorUuid()  )
2199       .append( "modifier",            this.getModifierUuid() )
2200       .toString();
2201   } // public String toString()
2202 
2203 
2204   // PROTECTED CLASS METHODS //
2205 
2206   /**
2207    * add root stem
2208    * @param s session
2209    * @param changed if you want to know if it was added, pass in array of size one, else null
2210    * @since   1.2.0
2211    * @return stem
2212    * @throws GrouperException is problem
2213    */
2214   public static Stem internal_addRootStem(GrouperSession s, boolean[] changed) 
2215     throws  GrouperException {
2216     Stem root = null;
2217     try {
2218       root = StemFinder.findByName(s, ROOT_INT, true);
2219     } catch (StemNotFoundException snfe) {
2220     
2221     }
2222     
2223     //dont add twice!
2224     if (root != null) {
2225       
2226       if (!StringUtils.equals(root.getDisplayExtensionDb(), ROOT_INT)) {
2227         throw new RuntimeException("Root display extension should be '" 
2228             + ROOT_INT + "' but is: '" + root.getDisplayExtensionDb() + "'" );
2229       }
2230       if (!StringUtils.equals(root.getDisplayNameDb(), ROOT_INT)) {
2231         throw new RuntimeException("Root display name should be '" 
2232             + ROOT_INT + "' but is: '" + root.getDisplayNameDb() + "'" );
2233       }
2234       if (!StringUtils.equals(root.getExtensionDb(), ROOT_INT)) {
2235         throw new RuntimeException("Root extension should be '" 
2236             + ROOT_INT + "' but is: '" + root.getExtensionDb() + "'" );
2237       }
2238       if (!StringUtils.equals(root.getNameDb(), ROOT_INT)) {
2239         throw new RuntimeException("Root name should be '" 
2240             + ROOT_INT + "' but is: '" + root.getNameDb() + "'" );
2241       }
2242       if (GrouperUtil.length(changed) > 0) {
2243         changed[0] = false;
2244       }
2245       return root;
2246     }
2247     if (GrouperUtil.length(changed) > 0) {
2248       changed[0] = false;
2249     }
2250     
2251     //note, no need for GrouperSession inverse of control
2252     try {
2253       root = new Stem();
2254       root.setCreatorUuid( s.getMember().getUuid() );
2255       root.setCreateTimeLong( new Date().getTime() );
2256       root.setDisplayExtensionDb(ROOT_INT);
2257       root.setDisplayNameDb(ROOT_INT);
2258       root.setExtensionDb(ROOT_INT);
2259       root.setNameDb(ROOT_INT);
2260       root.setUuid( GrouperUuid.getUuid() );
2261       GrouperDAOFactory.getFactory().getStem().createRootStem(root) ;
2262       return root;
2263     }
2264     catch (GrouperDAOException eDAO) {
2265       String msg = E.STEM_ROOTINSTALL + eDAO.getMessage();
2266       LOG.fatal(msg);
2267       throw new GrouperException(msg, eDAO);
2268     }
2269   } // protected static Stem internal_addRootStem(GrouperSession s)
2270 
2271   /** logger */
2272   private static final Log LOG = GrouperUtil.getLog(Stem.class);
2273 
2274   /**
2275    * set modified
2276    * @since   1.2.0
2277    */
2278   public void internal_setModified() {
2279     this.setModifierUuid( GrouperSession.staticGrouperSession().getMember().getUuid() );
2280     this.setModifyTimeLong(  new Date().getTime()    );
2281   } // protected void internal_setModified()
2282 
2283 
2284   // PROTECTED INSTANCE METHODS //
2285 
2286   /**
2287    * add child group with uuid
2288    * @param extn extension
2289    * @param dExtn display extension
2290    * @param uuid uuid
2291    * @return group 
2292    * @throws GroupAddException if problem 
2293    * @throws InsufficientPrivilegeException if problem 
2294    * @since   1.2.0
2295    */
2296   public Group internal_addChildGroup(final String extn, final String dExtn, final String uuid) 
2297     throws GroupAddException, InsufficientPrivilegeException {
2298     
2299     return internal_addChildGroup(extn, dExtn, uuid, null);
2300   }
2301   
2302   /**
2303    * add child group with uuid
2304    * @param extn extension
2305    * @param dExtn display extension
2306    * @param uuid uuid
2307    * @param typeOfGroup
2308    * @return group 
2309    * @throws GroupAddException if problem 
2310    * @throws InsufficientPrivilegeException if problem 
2311    */
2312   public Group internal_addChildGroup(final String extn, final String dExtn, final String uuid, final TypeOfGroup typeOfGroup) 
2313     throws GroupAddException, InsufficientPrivilegeException {
2314 
2315     Set types = new LinkedHashSet<GroupType>();
2316     
2317     return internal_addChildGroup(extn, dExtn, uuid, 
2318         null, types, new HashMap<String, String>(), true, typeOfGroup, true);    
2319   }
2320   
2321   /**
2322    * 
2323    * @param extn
2324    * @param dExtn
2325    * @param uuid
2326    * @param description
2327    * @param types
2328    * @param attributes
2329    * @param addDefaultGroupPrivileges
2330    * @param typeOfGroup or null for default
2331    * @param checkSecurity 
2332    * @return group
2333    * @throws GroupAddException
2334    * @throws InsufficientPrivilegeException
2335    */
2336   public Group internal_addChildGroup(final String extn, final String dExtn,
2337       final String uuid, final String description, final Set<GroupType> types,
2338       final Map<String, String> attributes, final boolean addDefaultGroupPrivileges, final TypeOfGroup typeOfGroup,
2339       final boolean checkSecurity)
2340       throws GroupAddException, InsufficientPrivilegeException {
2341     
2342     final String errorMessageSuffix = ", stem name: " + this.name + ", group extension: " + extn
2343       + ", group dExtension: " + dExtn + ", uuid: " + uuid + ", typeOfGroup: " + typeOfGroup;
2344     
2345     Group="../../../../edu/internet2/middleware/grouper/Group.html#Group">Group group = (Group)HibernateSession.callbackHibernateSession(
2346         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
2347         new HibernateHandler() {
2348   
2349           @SuppressWarnings("deprecation")
2350           public Object callback(HibernateHandlerBean hibernateHandlerBean)
2351               throws GrouperDAOException {
2352             
2353             try {
2354 
2355               hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
2356               
2357               StopWatch sw = new StopWatch();
2358               sw.start();
2359               if (checkSecurity) {
2360                 if (!PrivilegeHelper.canCreate(GrouperSession.staticGrouperSession(), 
2361                     Stem.this, GrouperSession.staticGrouperSession().getSubject())) {
2362                   throw new InsufficientPrivilegeException(E.CANNOT_CREATE + errorMessageSuffix + ", " + GrouperSession.staticGrouperSession());
2363                 } 
2364               }
2365               GrouperValidator v = AddGroupValidator.validate(Stem.this, extn, dExtn);
2366               if (v.isInvalid()) {
2367                 if (v.getErrorMessage().startsWith(AddGroupValidator.GROUP_ALREADY_EXISTS_WITH_NAME_PREFIX)) {
2368                   throw new GroupAddAlreadyExistsException(v.getErrorMessage() + errorMessageSuffix);
2369                 }
2370                 throw new GroupAddException( v.getErrorMessage() + errorMessageSuffix );
2371               }
2372         
2373               Groupe/grouper/Group.html#Group">Group _g = new Group();
2374               _g.setParentUuid(Stem.this.getUuid());
2375               if (GrouperLoader.isDryRun()) {
2376                 _g.setDisplayExtensionDb(dExtn);
2377                 _g.setExtensionDb(extn);
2378                 
2379               } else {
2380                 _g.setDisplayExtension(dExtn);
2381                 _g.setExtension(extn);
2382                 
2383               }
2384               _g.setDescription(description);
2385               _g.setCreateTimeLong(new Date().getTime());
2386               _g.setCreatorUuid(GrouperSession.staticGrouperSession().getMember().getUuid());
2387               _g.setTypes(types);
2388               
2389               if (typeOfGroup != null) {
2390                 _g.setTypeOfGroup(typeOfGroup);
2391               }
2392   
2393               v = NotNullOrEmptyValidator.validate(uuid);
2394               if (v.isInvalid()) {
2395                 _g.setUuid( GrouperUuid.getUuid() );
2396               }
2397               else {
2398                 _g.setUuid(uuid);
2399               }
2400         
2401               //CH 20080220: this will start saving the group
2402               //if loader dry run dont bother
2403               if (GrouperLoader.isDryRun()) {
2404                 GrouperLoader.dryRunWriteLine("Creating group: " + name);
2405                 
2406               } else {
2407                 GrouperSubjectj/GrouperSubject.html#GrouperSubject">GrouperSubject  subj  = new GrouperSubject(_g);
2408                 Member/grouper/Member.html#Member">Member _m = new Member();
2409                 _m.setSubjectIdDb( subj.getId() );
2410                 _m.setSubjectSourceIdDb( subj.getSource().getId() );
2411                 _m.setSubjectTypeId( subj.getType().getName() );
2412                 _m.updateMemberAttributes(subj, false);
2413                 // TODO 20070328 this is incredibly ugly.  making it even worse is that i am also checking
2414                 //               for existence in the dao as well.
2415                 if (uuid == null) {
2416                   _m.setUuid( GrouperUuid.getUuid() ); // assign a new uuid
2417                 }
2418                 else {
2419                   try {
2420                     // member already exists.  use existing uuid.
2421                     _m.setUuid( GrouperDAOFactory.getFactory().getMember().findBySubject(subj, true).getUuid() );
2422                   }
2423                   catch (MemberNotFoundException eMNF) {
2424                     // couldn't find member.  assign new uuid.
2425                     _m.setUuid( GrouperUuid.getUuid() ); 
2426                   }
2427                 }
2428         
2429 
2430                 GrouperDAOFactory.getFactory().getStem().createChildGroup(Stem.this, _g, _m);
2431                 hibernateHandlerBean.getHibernateSession().misc().flush();
2432                 
2433                 if (addDefaultGroupPrivileges) {
2434                   _grantDefaultPrivsUponCreate(_g);
2435                   PerformanceLogger.performanceTimingGate(GroupSave.PERFORMANCE_LOG_LABEL, "defaultPrivs");
2436                 }
2437                 
2438                 // take care of attributes now that default privs have been added
2439                 for (GroupType groupType : _g.getTypesDb()) {    
2440                   _g.getAttributeDelegate().internal_assignAttributeHelper(null, groupType.getAttributeDefName(), checkSecurity, null, null);
2441                 }
2442                 
2443                 //loop through in case an attribute is set in hook
2444                 if (attributes != null) {
2445                   for (String key : attributes.keySet()) {
2446                     _g.setAttribute(key, attributes.get(key), checkSecurity);
2447                   }
2448                 }
2449                 PerformanceLogger.performanceTimingGate(GroupSave.PERFORMANCE_LOG_LABEL, "attributes");
2450                 
2451                 //fire a rule
2452                 RulesGroupBean/RulesGroupBean.html#RulesGroupBean">RulesGroupBean rulesGroupBean = new RulesGroupBean(_g);
2453                 //fire rules directly connected to this membership remove
2454                 RuleEngine.fireRule(RuleCheckType.groupCreate, rulesGroupBean);
2455 
2456                 PerformanceLogger.performanceTimingGate(GroupSave.PERFORMANCE_LOG_LABEL, "groupCreateRule");
2457 
2458               }
2459               
2460               
2461               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
2462                 AuditEntry auditEntry = null;
2463                 
2464                 if (typeOfGroup == TypeOfGroup.entity) {
2465                   auditEntry = new AuditEntry(AuditTypeBuiltin.ENTITY_ADD, "id", 
2466                       _g.getUuid(), "name", _g.getName(), "parentStemId", Stem.this.getUuid(), "displayName", 
2467                       _g.getDisplayName(), "description", _g.getDescription());
2468                   auditEntry.setDescription("Added entity: " + _g.getName());
2469                   
2470                 } else {
2471                   auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_ADD, "id", 
2472                       _g.getUuid(), "name", _g.getName(), "parentStemId", Stem.this.getUuid(), "displayName", 
2473                       _g.getDisplayName(), "description", _g.getDescription());
2474                   auditEntry.setDescription("Added group: " + _g.getName());
2475                   
2476                 }
2477                 auditEntry.saveOrUpdate(true);
2478                 PerformanceLogger.performanceTimingGate(GroupSave.PERFORMANCE_LOG_LABEL, "audit");
2479 
2480               }
2481               
2482               sw.stop();
2483               EventLog.info(GrouperSession.staticGrouperSession(), M.GROUP_ADD + Quote.single(_g.getName()), sw);
2484               
2485               return _g;
2486             } catch (GrouperDAOException eDAO) {
2487               throw new GroupAddException( E.CANNOT_CREATE_GROUP + errorMessageSuffix + eDAO.getMessage(), eDAO );
2488             } catch (SourceUnavailableException eSU)  {
2489               throw new GroupAddException(E.CANNOT_CREATE_GROUP + errorMessageSuffix + eSU.getMessage(), eSU);
2490             }
2491           }
2492         });
2493     
2494     return group;
2495   }
2496 
2497   /**
2498    * @param attributeDef
2499    * @param session
2500    * @param extension
2501    * @param displayExtension
2502    * @param id
2503    * @param description
2504    * @return group
2505    * @throws AttributeDefNameAddException
2506    * @throws InsufficientPrivilegeException
2507    */
2508   public AttributeDefName internal_addChildAttributeDefName(final GrouperSession session, final AttributeDef attributeDef,
2509       final String extension, final String displayExtension,
2510       final String id, final String description)
2511       throws InsufficientPrivilegeException {
2512 
2513     final String errorMessageSuffix = ", stem name: " + this.name + ", attrDefName extension: " + extension
2514       + ", uuid: " + id + ", ";
2515 
2516     return (AttributeDefName)HibernateSession.callbackHibernateSession(
2517         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
2518         new HibernateHandler() {
2519 
2520           public Object callback(HibernateHandlerBean hibernateHandlerBean)
2521               throws GrouperDAOException {
2522             try {
2523 
2524               hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
2525 
2526               StopWatch sw = new StopWatch();
2527               sw.start();
2528               if (!PrivilegeHelper.canCreate(session,
2529                   Stem.this, session.getSubject())) {
2530                 throw new InsufficientPrivilegeException(E.CANNOT_CREATE + errorMessageSuffix);
2531               }
2532               GrouperValidator v = AddAttributeDefNameValidator.validate(Stem.this, extension);
2533               if (v.isInvalid()) {
2534                 throw new AttributeDefNameAddException( v.getErrorMessage() + errorMessageSuffix );
2535               }
2536         
2537               AttributeDefNameefName.html#AttributeDefName">AttributeDefName attributeDefName = new AttributeDefName();
2538               attributeDefName.setAttributeDefId(attributeDef.getId());
2539               attributeDefName.setStemId(Stem.this.getUuid());
2540               attributeDefName.setExtensionDb(extension);
2541               attributeDefName.setDisplayExtensionDb(displayExtension);
2542               attributeDefName.setDescription(description);
2543               attributeDefName.setNameDb(Stem.this.getName() + ":" + extension);
2544               attributeDefName.setDisplayNameDb(Stem.this.getDisplayName() + ":" + displayExtension);
2545 
2546               String theId = id;
2547               if (StringUtils.isBlank(theId)) {
2548                 theId = GrouperUuid.getUuid();
2549               }
2550               
2551               attributeDefName.setId(theId);
2552                               
2553               //CH 20080220: this will start saving the attributeDefName
2554               GrouperDAOFactory.getFactory().getStem().createChildAttributeDefName( Stem.this, attributeDefName );
2555                 
2556               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
2557                 AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.ATTRIBUTE_DEF_NAME_ADD, "id", 
2558                     attributeDefName.getId(), "name", attributeDefName.getName(), 
2559                     "displayName", attributeDefName.getDisplayName(),
2560                     "description", attributeDefName.getDescription(),
2561                     "parentStemId", Stem.this.getUuid(), 
2562                     "parentAttributeDefId", attributeDef.getId(),
2563                     "parentAttributeDefName", attributeDef.getName()
2564                 );
2565                 auditEntry.setDescription("Added attributeDefName: " + attributeDefName.getName());
2566                 auditEntry.saveOrUpdate(true);
2567               }
2568               
2569               sw.stop();
2570               
2571               return attributeDefName;
2572             } catch (HookVeto hv) {
2573               throw hv;
2574             } catch (AttributeDefNameAddException adnae) {
2575               throw adnae;
2576             } catch (Exception e) {
2577               throw new AttributeDefNameAddException( "Cannot create attribute def name: " + errorMessageSuffix + e.getMessage(), e );
2578             }
2579           }
2580         });
2581   } 
2582 
2583   
2584   /**
2585    * 
2586    * @param session
2587    * @param extn
2588    * @param id
2589    * @param attributeDefType 
2590    * @param description
2591    * @return group
2592    * @throws AttributeDefAddException
2593    * @throws InsufficientPrivilegeException
2594    */
2595   public AttributeDef internal_addChildAttributeDef(final GrouperSession session, final String extn,
2596       final String id, final AttributeDefType attributeDefType, final String description)
2597       throws InsufficientPrivilegeException {
2598     
2599     final String errorMessageSuffix = ", stem name: " + this.name + ", attrDef extension: " + extn
2600       + ", uuid: " + id + ", ";
2601     
2602     AttributeDefedu/internet2/middleware/grouper/attr/AttributeDef.html#AttributeDef">AttributeDef attributeDef = (AttributeDef)HibernateSession.callbackHibernateSession(
2603         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
2604         new HibernateHandler() {
2605 
2606           public Object callback(HibernateHandlerBean hibernateHandlerBean)
2607               throws GrouperDAOException {
2608             try {
2609 
2610               hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
2611 
2612               StopWatch sw = new StopWatch();
2613               sw.start();
2614               if (!PrivilegeHelper.canCreate(session, 
2615                   Stem.this, session.getSubject())) {
2616                 throw new InsufficientPrivilegeException(E.CANNOT_CREATE + errorMessageSuffix);
2617               } 
2618               GrouperValidator v = AddAttributeDefValidator.validate(Stem.this, extn);
2619               if (v.isInvalid()) {
2620                 throw new AttributeDefAddException( v.getErrorMessage() + errorMessageSuffix );
2621               }
2622         
2623               AttributeDeftributeDef.html#AttributeDef">AttributeDef attributeDef = new AttributeDef();
2624               attributeDef.setStemId(Stem.this.getUuid());
2625               attributeDef.setExtensionDb(extn);
2626               attributeDef.setDescription(description);
2627               attributeDef.setNameDb(Stem.this.getName() + ":" + extn);
2628               attributeDef.setAttributeDefType(attributeDefType);
2629 
2630               String theId = id;
2631               if (StringUtils.isBlank(theId)) {
2632                 theId = GrouperUuid.getUuid();
2633               }
2634               
2635               attributeDef.setId(theId);
2636                               
2637               //CH 20080220: this will start saving the attributeDef
2638               GrouperDAOFactory.getFactory().getStem().createChildAttributeDef( Stem.this, attributeDef );
2639                 
2640               //default action
2641               attributeDef.getAttributeDefActionDelegate().addAction("assign");
2642               
2643               //grant privs
2644               _grantDefaultPrivsUponCreate(attributeDef);
2645               
2646               //fire a rule
2647               RulesAttributeDefBeaneDefBean.html#RulesAttributeDefBean">RulesAttributeDefBean rulesAttributeDefBean = new RulesAttributeDefBean(attributeDef);
2648               //fire rules directly connected to this membership remove
2649               RuleEngine.fireRule(RuleCheckType.attributeDefCreate, rulesAttributeDefBean);
2650 
2651               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
2652                 AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.ATTRIBUTE_DEF_ADD, "id", 
2653                     attributeDef.getId(), "name", attributeDef.getName(), "parentStemId", Stem.this.getUuid(), 
2654                     "description", attributeDef.getDescription());
2655                 auditEntry.setDescription("Added attributeDef: " + attributeDef.getName());
2656                 auditEntry.saveOrUpdate(true);
2657               }
2658               
2659               sw.stop();
2660               
2661               return attributeDef;
2662             } catch (HookVeto hv) {
2663               GrouperUtil.injectInException(hv, "Cannot create attribute def: " + errorMessageSuffix);
2664               throw hv;
2665             } catch (Exception e) {
2666               throw new AttributeDefAddException( "Cannot create attribute def: " + errorMessageSuffix + ", " + e.getMessage(), e );
2667             }
2668           }
2669         });
2670     
2671     return attributeDef;
2672     
2673   } 
2674 
2675   /**
2676    * add child stem with uuid
2677    * @since   1.2.0
2678    * @param extn extension
2679    * @param dExtn display extension
2680    * @param uuid uuid
2681    * @return the new stem
2682    * @throws StemAddException if problem
2683    * @throws InsufficientPrivilegeException if problem
2684    */
2685   public Stem internal_addChildStem(final String extn, final String dExtn,
2686       final String uuid) throws StemAddException, InsufficientPrivilegeException {
2687     return internal_addChildStem(GrouperSession.staticGrouperSession(), extn, dExtn,
2688         uuid, true, true);
2689   }
2690 
2691   
2692   /**
2693    * keep a map of strings to synchronize on
2694    */
2695   private static GrouperCache<String, String> stemLocksCache = null;
2696   
2697   /**
2698    * stem locks cache
2699    * @return the cache
2700    */
2701   private static GrouperCache<String, String> stemLocksCache() {
2702     if (stemLocksCache == null) {
2703       synchronized(Stem.class) {
2704         if (stemLocksCache == null) {
2705           stemLocksCache = new GrouperCache<String, String>(Stem.class.getName() + ".stemLocksCache");
2706         }
2707       }
2708     }
2709     return stemLocksCache;
2710   }
2711   
2712   /**
2713    * keep track of when stem is created
2714    */
2715   private static GrouperCache<String, Boolean> stemCreatedCache = null;
2716   
2717   /**
2718    * stem created cache
2719    * @return the cache
2720    */
2721   private static GrouperCache<String, Boolean> stemCreatedCache() {
2722     if (stemCreatedCache == null) {
2723       synchronized(Stem.class) {
2724         if (stemCreatedCache == null) {
2725           stemCreatedCache = new GrouperCache<String, Boolean>(Stem.class.getName() + ".stemCreatedCache");
2726         }
2727       }
2728     }
2729     return stemCreatedCache;
2730   }
2731   
2732   /**
2733    * 
2734    * @param session
2735    * @param extn
2736    * @param dExtn
2737    * @param uuid
2738    * @param addDefaultStemPrivileges
2739    * @param failIfExists 
2740    * @return stem
2741    * @throws StemAddException
2742    * @throws InsufficientPrivilegeException
2743    */
2744   protected Stem internal_addChildStem(final GrouperSession session, final String extn, 
2745       final String dExtn, final String uuid, final boolean addDefaultStemPrivileges, final boolean failIfExists) 
2746     throws  StemAddException,
2747             InsufficientPrivilegeException {
2748     
2749     if (StringUtils.isBlank(extn) || extn.contains(":")) {
2750       throw new StemAddException("extension cant be blank or contain colon: '" + extn + "'");
2751     }
2752     
2753     String stemName = U.constructName( Stem.this.getName(), extn );
2754     
2755     Stem stem = StemFinder.findByName(session.internal_getRootSession(), stemName, false, new QueryOptions().secondLevelCache(false));
2756 
2757     if (stem != null && failIfExists) {
2758       throw new StemAddAlreadyExistsException("Stem exists: " + stemName);
2759     }
2760     
2761     if (stem == null) {
2762       
2763       synchronized (Stem.class) {
2764         //get the same key for name
2765         if (!stemLocksCache().containsKey(stemName)) {
2766           stemLocksCache().put(stemName, stemName);
2767         }
2768         stemName = stemLocksCache().get(stemName);
2769       }
2770       final String STEM_NAME = stemName;
2771 
2772       synchronized(stemName) {
2773         
2774         //something created this recently
2775         if (stemCreatedCache().get(stemName) != null) {
2776 
2777           //wait for the transaction to finish...
2778           for (int i=0;i<20;i++) {
2779 
2780             //in transaction
2781             stem = StemFinder.findByName(session.internal_getRootSession(), STEM_NAME, false, new QueryOptions().secondLevelCache(false));
2782             
2783             if (stem != null) {
2784 
2785               if (failIfExists) {
2786                 throw new StemAddAlreadyExistsException("Stem exists: " + stemName);
2787               }
2788               
2789               break;
2790             }
2791 
2792             //out of transaction
2793             stem = (Stem)GrouperTransaction.callbackGrouperTransaction(GrouperTransactionType.NONE, new GrouperTransactionHandler() {
2794               
2795               public Object callback(GrouperTransaction grouperTransaction)
2796                   throws GrouperDAOException {
2797                 return StemFinder.findByName(session.internal_getRootSession(), STEM_NAME, false, new QueryOptions().secondLevelCache(false));
2798               }
2799             });
2800             
2801             if (stem != null) {
2802               
2803               if (failIfExists) {
2804                 throw new StemAddAlreadyExistsException("Stem exists: " + stemName);
2805               }
2806               
2807               // if its in another transaction... wait a couple seconds for it to finish
2808               GrouperUtil.sleep(2000);
2809 
2810               break;
2811             }
2812 
2813             GrouperUtil.sleep(1000);
2814           }
2815           
2816         }
2817         
2818         
2819         if (stem == null) {
2820 
2821           //race conditions
2822           stemCreatedCache().put(stemName, Boolean.TRUE);
2823           
2824           stem = (Stem)HibernateSession.callbackHibernateSession(
2825               GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
2826               new HibernateHandler() {
2827 
2828                 public Object callback(HibernateHandlerBean hibernateHandlerBean)
2829                     throws GrouperDAOException {
2830 
2831                   hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
2832 
2833                   StopWatch sw = new StopWatch();
2834                   sw.start();
2835                   if ( !GrouperLoader.isDryRun() && !PrivilegeHelper.canCreate(GrouperSession.staticGrouperSession(), Stem.this, session.getSubject())) {
2836                     throw new InsufficientPrivilegeException(E.CANNOT_CREATE + ", "
2837                         + GrouperUtil.toStringSafe(Stem.this) + ", extn: " + extn + ", dExtn: " 
2838                         + dExtn + ", uuid: " + uuid + ", subject: " + session.getSubject());
2839                   } 
2840                   GrouperValidator v = AddStemValidator.validate(Stem.this, extn, dExtn);
2841                   if (v.isInvalid()) {
2842                     String errorMessage = StringUtils.defaultString(v.getErrorMessage());
2843                     if (errorMessage.startsWith(AddStemValidator.STEM_ALREADY_EXISTS_ERROR_MESSAGE)) {
2844                       throw new StemAddAlreadyExistsException(errorMessage);
2845                     }
2846                     throw new StemAddException( errorMessage );
2847                   }
2848                   try {
2849                     Steme/grouper/Stem.html#Stem">Stem _ns = new Stem();
2850                     _ns.setCreatorUuid( session.getMember().getUuid() );
2851                     _ns.setCreateTimeLong( new Date().getTime() );
2852                     _ns.setDisplayExtensionDb(dExtn);
2853                     _ns.setDisplayNameDb( U.constructName( Stem.this.getDisplayName(), dExtn ) );
2854                     _ns.setExtensionDb(extn);
2855                     _ns.setNameDb(STEM_NAME );
2856                     _ns.setParentUuid( Stem.this.getUuid() );
2857                     
2858                     v = NotNullOrEmptyValidator.validate(uuid);
2859                     if (v.isInvalid()) {
2860                       _ns.setUuid( GrouperUuid.getUuid() );
2861                     }
2862                     else {
2863                       _ns.setUuid(uuid);
2864                     }
2865                     
2866                     if (!GrouperLoader.isDryRun()) {
2867                       GrouperDAOFactory.getFactory().getStem().createChildStem( _ns ) ;
2868         
2869         
2870                       if (addDefaultStemPrivileges) {
2871                         _grantDefaultPrivsUponCreate(_ns);
2872                       }
2873         
2874                       //fire a rule
2875                       RulesStemBeanns/RulesStemBean.html#RulesStemBean">RulesStemBean rulesStemBean = new RulesStemBean(_ns);
2876                       //fire rules directly connected to this membership remove
2877                       RuleEngine.fireRule(RuleCheckType.stemCreate, rulesStemBean);
2878         
2879                       
2880                       if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
2881                         AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.STEM_ADD, "id", 
2882                             _ns.getUuid(), "name", _ns.getName(), "parentStemId", Stem.this.getUuid(), "displayName", 
2883                             _ns.getDisplayName(), "description", _ns.getDescription());
2884                         auditEntry.setDescription("Added stem: " + _ns.getName());
2885                         auditEntry.saveOrUpdate(true);
2886                       }
2887                     } 
2888                     
2889                     sw.stop();
2890                     EventLog.info(session, M.STEM_ADD + Quote.single( _ns.getName() ), sw);
2891 
2892                     return _ns;
2893                   } catch (StemAddException e) {
2894                     String error = "Problem creating child stem: " + GrouperUtil.toStringSafe(Stem.this)
2895                       + ", extn: " + extn + ", dExtn: " + dExtn + ", uuid: " + uuid + ", " + e.getMessage();
2896                     GrouperUtil.injectInException(e, error);
2897                     throw e;
2898                   } catch (GrouperDAOException e) {
2899                     String error = "Problem creating child stem: " + GrouperUtil.toStringSafe(Stem.this)
2900                       + ", extn: " + extn + ", dExtn: " + dExtn + ", uuid: " + uuid + ", " + e.getMessage();
2901                     GrouperUtil.injectInException(e, error);
2902                     throw new StemAddException(E.CANNOT_CREATE_STEM + e.getMessage(), e);
2903                   }
2904                 }
2905           });
2906         }
2907       }
2908     }
2909     
2910     return stem;
2911   }
2912 
2913   /**
2914    * <pre>
2915    * Now grant ADMIN (as root) to the creator of the child group.
2916    *
2917    * Ideally this would be wrapped up in the broader transaction
2918    * of adding the child stem but as the interfaces may be
2919    * outside of our control, I don't think we can do that.  
2920    *
2921    * Possibly a bug. The modify* attrs get set when granting ADMIN at creation.
2922    * </pre>
2923    * @param g group
2924    * @throws GroupAddException if problem
2925    */
2926   private void _grantDefaultPrivsUponCreate(Group g)
2927     throws  GroupAddException
2928   {
2929     try {
2930       boolean assignAdminToWheelOrRootOnCreate = GrouperConfig.retrieveConfig().propertyValueBoolean("privileges.assignAdminToWheelOrRootOnCreate", false);
2931       
2932       if (assignAdminToWheelOrRootOnCreate || !PrivilegeHelper.isWheelOrRoot(GrouperSession.staticGrouperSession().getSubject())) {
2933         
2934         boolean assignAdminToInheritedAdminsOnCreate = GrouperConfig.retrieveConfig().propertyValueBoolean("privileges.assignAdminToInheritedAdminsOnCreate", false);
2935         if (assignAdminToInheritedAdminsOnCreate || !RuleApi.hasInheritedPrivilege(g, GrouperSession.staticGrouperSession().getSubject(), AccessPrivilege.ADMIN, true)) {
2936         
2937           GrouperSession.staticGrouperSession().internal_getRootSession().getAccessResolver().grantPrivilege(
2938             g, GrouperSession.staticGrouperSession().getSubject(), AccessPrivilege.ADMIN, null   
2939           );
2940         }
2941       }
2942       
2943       // Now optionally grant other privs
2944       if (g.getTypeOfGroup() != TypeOfGroup.entity) {
2945         this._grantOptionalPrivUponCreate( g, AccessPrivilege.VIEW, GrouperConfig.GCGAV );
2946         this._grantOptionalPrivUponCreate( g, AccessPrivilege.OPTIN, GrouperConfig.GCGAOI );
2947         this._grantOptionalPrivUponCreate( g, AccessPrivilege.OPTOUT, GrouperConfig.GCGAOO );
2948         this._grantOptionalPrivUponCreate( g, AccessPrivilege.READ, GrouperConfig.GCGAR );
2949         this._grantOptionalPrivUponCreate( g, AccessPrivilege.GROUP_ATTR_READ, GrouperConfig.GCGAGAR );
2950       }
2951       if (g.getTypeOfGroup() == TypeOfGroup.entity) {
2952         this._grantOptionalPrivUponCreate( g, AccessPrivilege.VIEW, "entities.create.grant.all.view" );
2953       }
2954     }
2955     catch (GrantPrivilegeException eGP)         {
2956       throw new GroupAddException(eGP.getMessage(), eGP);
2957     }
2958     catch (InsufficientPrivilegeException eIP)  {
2959       throw new GroupAddException(eIP.getMessage(), eIP);
2960     }
2961     catch (SchemaException eS)                  {
2962       throw new GroupAddException(eS.getMessage(), eS);
2963     }
2964     catch (UnableToPerformException eUTP) {
2965       throw new GroupAddException( eUTP.getMessage(), eUTP );
2966     }
2967   } 
2968   
2969   /**
2970    * Now grant ADMIN (as root) to the creator of the attributeDef.
2971    *
2972    * @param attributeDef stem
2973    * @throws AttributeDefAddException if problem
2974    */
2975   private void _grantDefaultPrivsUponCreate(AttributeDef attributeDef) throws  AttributeDefAddException {
2976     try {
2977       boolean assignAdminToWheelOrRootOnCreate = GrouperConfig.retrieveConfig().propertyValueBoolean("privileges.assignAdminToWheelOrRootOnCreate", false);
2978       
2979       if (assignAdminToWheelOrRootOnCreate || !PrivilegeHelper.isWheelOrRoot(GrouperSession.staticGrouperSession().getSubject())) {
2980         boolean assignAdminToInheritedAdminsOnCreate = GrouperConfig.retrieveConfig().propertyValueBoolean("privileges.assignAdminToInheritedAdminsOnCreate", false);
2981         if (assignAdminToInheritedAdminsOnCreate || !RuleApi.hasInheritedPrivilege(attributeDef, GrouperSession.staticGrouperSession().getSubject(), AttributeDefPrivilege.ATTR_ADMIN, true)) {
2982           //whoever created this is an admin
2983           GrouperSession.staticGrouperSession().internal_getRootSession().getAttributeDefResolver().grantPrivilege(
2984             attributeDef, GrouperSession.staticGrouperSession().getSubject(), AttributeDefPrivilege.ATTR_ADMIN, null);
2985         }
2986       }
2987       
2988       // Now optionally grant other privs
2989       this._grantOptionalPrivUponCreate(
2990         attributeDef, AttributeDefPrivilege.ATTR_ADMIN, GrouperConfig.ATTRIBUTE_DEFS_CREATE_GRANT_ALL_ATTR_ADMIN
2991       );
2992       this._grantOptionalPrivUponCreate(
2993           attributeDef, AttributeDefPrivilege.ATTR_OPTIN, GrouperConfig.ATTRIBUTE_DEFS_CREATE_GRANT_ALL_ATTR_OPTIN
2994         );
2995       this._grantOptionalPrivUponCreate(
2996           attributeDef, AttributeDefPrivilege.ATTR_OPTOUT, GrouperConfig.ATTRIBUTE_DEFS_CREATE_GRANT_ALL_ATTR_OPTOUT
2997         );
2998       this._grantOptionalPrivUponCreate(
2999           attributeDef, AttributeDefPrivilege.ATTR_READ, GrouperConfig.ATTRIBUTE_DEFS_CREATE_GRANT_ALL_ATTR_READ
3000         );
3001       this._grantOptionalPrivUponCreate(
3002           attributeDef, AttributeDefPrivilege.ATTR_UPDATE, GrouperConfig.ATTRIBUTE_DEFS_CREATE_GRANT_ALL_ATTR_UPDATE
3003         );
3004       this._grantOptionalPrivUponCreate(
3005           attributeDef, AttributeDefPrivilege.ATTR_VIEW, GrouperConfig.ATTRIBUTE_DEFS_CREATE_GRANT_ALL_ATTR_VIEW
3006         );
3007       this._grantOptionalPrivUponCreate(
3008           attributeDef, AttributeDefPrivilege.ATTR_DEF_ATTR_READ, GrouperConfig.ATTRIBUTE_DEFS_CREATE_GRANT_ALL_ATTR_DEF_ATTR_READ
3009         );
3010       this._grantOptionalPrivUponCreate(
3011           attributeDef, AttributeDefPrivilege.ATTR_DEF_ATTR_UPDATE, GrouperConfig.ATTRIBUTE_DEFS_CREATE_GRANT_ALL_ATTR_DEF_ATTR_UPDATE
3012         );
3013     }
3014     catch (GrantPrivilegeException eGP)         {
3015       throw new AttributeDefAddException(eGP.getMessage(), eGP);
3016     }
3017     catch (InsufficientPrivilegeException eIP)  {
3018       throw new AttributeDefAddException(eIP.getMessage(), eIP);
3019     }
3020     catch (SchemaException eS)                  {
3021       throw new AttributeDefAddException(eS.getMessage(), eS);
3022     }
3023     catch (UnableToPerformException eUTP) {
3024       throw new AttributeDefAddException( eUTP.getMessage(), eUTP );
3025     }
3026   } 
3027 
3028   /**
3029    * Now grant STEM (as root) to the creator of the child stem.
3030    *
3031    * Ideally this would be wrapped up in the broader transaction
3032    * of adding the child stem but as the interfaces may be
3033    * outside of our control, I don't think we can do that.  
3034    *
3035    * Possibly a bug. The modify* attrs get set when granting privs at creation.
3036    * 
3037    * @param ns stem
3038    * @throws StemAddException if problem
3039    */
3040   private void _grantDefaultPrivsUponCreate(Stem ns)
3041     throws  StemAddException
3042   {
3043     try {
3044       boolean assignAdminToWheelOrRootOnCreate = GrouperConfig.retrieveConfig().propertyValueBoolean("privileges.assignAdminToWheelOrRootOnCreate", false);
3045       
3046       if (assignAdminToWheelOrRootOnCreate || !PrivilegeHelper.isWheelOrRoot(GrouperSession.staticGrouperSession().getSubject())) {
3047         
3048         boolean assignAdminToInheritedAdminsOnCreate = GrouperConfig.retrieveConfig().propertyValueBoolean("privileges.assignAdminToInheritedAdminsOnCreate", false);
3049         if (assignAdminToInheritedAdminsOnCreate || !RuleApi.hasInheritedPrivilege(ns, GrouperSession.staticGrouperSession().getSubject(), NamingPrivilege.STEM_ADMIN, true)) {
3050 
3051           GrouperSession.staticGrouperSession().internal_getRootSession().getNamingResolver().grantPrivilege(
3052             ns, GrouperSession.staticGrouperSession().getSubject(), NamingPrivilege.STEM_ADMIN, null
3053           );
3054         }
3055       }
3056       
3057       // Now optionally grant other privs
3058       this._grantOptionalPrivUponCreate(
3059         ns, NamingPrivilege.CREATE, GrouperConfig.SCGAC
3060       );
3061       this._grantOptionalPrivUponCreate(
3062         ns, NamingPrivilege.STEM_ADMIN, GrouperConfig.SCGASA
3063       );
3064       this._grantOptionalPrivUponCreate(
3065           ns, NamingPrivilege.STEM_ATTR_READ, GrouperConfig.SCGASAR
3066         );
3067       this._grantOptionalPrivUponCreate(
3068           ns, NamingPrivilege.STEM_ATTR_UPDATE, GrouperConfig.SCGASAU
3069         );
3070     }
3071     catch (GrantPrivilegeException eGP)         {
3072       throw new StemAddException(eGP.getMessage(), eGP);
3073     }
3074     catch (InsufficientPrivilegeException eIP)  {
3075       throw new StemAddException(eIP.getMessage(), eIP);
3076     }
3077     catch (SchemaException eS)                  {
3078       throw new StemAddException(eS.getMessage(), eS);
3079     }
3080     catch (UnableToPerformException eUTP) {
3081       throw new StemAddException( eUTP.getMessage(), eUTP );
3082     }
3083   } 
3084 
3085   /**
3086    * grant optional priv upon create
3087    * @param o object
3088    * @param p prov
3089    * @param opt opt
3090    * @throws GrantPrivilegeException if problem 
3091    * @throws  IllegalStateException if <i>o</i> is neither group nor stem.
3092    * @throws InsufficientPrivilegeException if not privs
3093    * @throws SchemaException if problem
3094    * @throws UnableToPerformException if problem
3095    * @since   1.2.1
3096    */
3097   private void _grantOptionalPrivUponCreate(Object o, Privilege p, String opt) 
3098     throws  GrantPrivilegeException,
3099             IllegalStateException,
3100             InsufficientPrivilegeException,
3101             SchemaException,
3102             UnableToPerformException
3103   {
3104     Subject       all = SubjectFinder.findAllSubject();
3105     if (StringUtils.equals(GrouperConfig.retrieveConfig().propertyValueString(opt), GrouperConfig.BT)) {
3106       StopWatch sw = new StopWatch();
3107       sw.start();
3108       if      (o instanceof Group) {
3109         Grouphref="../../../../edu/internet2/middleware/grouper/Group.html#Group">Group g = (Group) o;
3110         GrouperSession.staticGrouperSession().getAccessResolver().grantPrivilege(g, all, p, null);
3111         sw.stop();
3112         EL.groupGrantPriv(GrouperSession.staticGrouperSession(), g.getName(), all, p, sw);
3113       }
3114       else if (o instanceof Stem) {
3115         Stemhref="../../../../edu/internet2/middleware/grouper/Stem.html#Stem">Stem ns = (Stem) o;
3116         GrouperSession.staticGrouperSession().getNamingResolver().grantPrivilege(ns, all, p, null);
3117         sw.stop();
3118         EL.stemGrantPriv(GrouperSession.staticGrouperSession(), ns.getName(), all, p, sw);
3119       }
3120       else if (o instanceof AttributeDef) {
3121         AttributeDefedu/internet2/middleware/grouper/attr/AttributeDef.html#AttributeDef">AttributeDef attributeDef = (AttributeDef) o;
3122         GrouperSession.staticGrouperSession().getAttributeDefResolver().grantPrivilege(attributeDef, all, p, null);
3123         sw.stop();
3124       }
3125       else {
3126         throw new IllegalStateException("unexpected condition: object is not group or stem: " + o);
3127       }
3128     }
3129   } 
3130 
3131   /**
3132    * rename child groups
3133    * @since   1.2.0
3134    * @param nameChange
3135    * @param displayNameChange
3136    * @param modifier
3137    * @param modifyTime
3138    * @param setAlternateName
3139    * @return the set of Group's
3140    */
3141   private Set _renameChildGroups(boolean nameChange, boolean displayNameChange, String modifier, long modifyTime, 
3142       boolean setAlternateName) {
3143     
3144     Set                 groups  = new LinkedHashSet();
3145     Iterator<Group> it = GrouperDAOFactory.getFactory().getStem().findAllChildGroups(this, Stem.Scope.ONE).iterator();
3146     while (it.hasNext()) {
3147       Groupref="../../../../edu/internet2/middleware/grouper/Group.html#Group">Group _g = (Group) it.next();
3148       
3149       if (displayNameChange) {
3150         _g.setDisplayNameDb(U.constructName(this.getDisplayName(), _g.getDisplayExtension()));
3151       }
3152 
3153       if (nameChange) {
3154         if (setAlternateName) {
3155           _g.internal_addAlternateName(_g.dbVersion().getNameDb(), false);
3156         }
3157         
3158         _g.setNameDb(U.constructName(this.getName(), _g.getExtension()));
3159       }
3160       
3161       _g.setModifierUuid(modifier);
3162       _g.setModifyTimeLong(modifyTime);
3163       _g.setDontSetModified(true);
3164       groups.add(_g);
3165     }
3166     return groups;
3167   } 
3168 
3169   /**
3170    * rename children.
3171    * @since   1.2.0
3172    * @param nameChange
3173    * @param displayNameChange
3174    * @param setAlternateName
3175    * @return set of stems and groups
3176    * @throws StemModifyException if problem
3177    */
3178   private Set _renameChildren(boolean nameChange, boolean displayNameChange, boolean setAlternateName)
3179     throws  StemModifyException {
3180     
3181     // rename child groups, stems, attributeDefs, and attributeDefNames
3182     Set     children    = new LinkedHashSet();
3183     String  modifier    = GrouperSession.staticGrouperSession().getMember().getUuid();
3184     long    modifyTime  = new Date().getTime();
3185     children.addAll(this._renameAttr(nameChange, displayNameChange));
3186     children.addAll(this._renameChildGroups(nameChange, displayNameChange, modifier, modifyTime, setAlternateName));
3187     children.addAll(this._renameChildStemsAndGroups(nameChange, displayNameChange, modifier, modifyTime, setAlternateName));
3188     return children;
3189   } 
3190 
3191   /**
3192    * rename child stems and groups.
3193    * @param nameChange
3194    * @param displayNameChange
3195    * @param modifier modifier
3196    * @param modifyTime modify time
3197    * @param setAlternateName
3198    * @return the set of stems and groups
3199    * @throws IllegalStateException if problem
3200    */
3201   private Set _renameChildStemsAndGroups(boolean nameChange, boolean displayNameChange, String modifier, 
3202       long modifyTime, boolean setAlternateName) throws IllegalStateException {
3203     
3204     Set       children  = new LinkedHashSet();
3205     Iterator<Stem> it = GrouperDAOFactory.getFactory().getStem().findAllChildStems(this, Scope.ONE, null, false).iterator();
3206     
3207     while (it.hasNext()) {
3208       Stem child = it.next();
3209       
3210       if (displayNameChange) {
3211         child.setDisplayNameDb(U.constructName(this.getDisplayNameDb(), child.getDisplayExtensionDb()));
3212       }
3213     
3214       if (nameChange) {
3215         if (setAlternateName) {
3216           child.internal_addAlternateName(child.dbVersion().getNameDb());
3217         }
3218         
3219         child.setNameDb(U.constructName(this.getNameDb(), child.getExtensionDb()));
3220       }
3221       
3222       // rename child stem
3223       child.setModifierUuid(modifier);
3224       child.setModifyTimeLong(modifyTime);
3225       children.add(child);
3226       
3227       // rename attributeDef and attributeDefName
3228       children.addAll(child._renameAttr(nameChange, displayNameChange));
3229 
3230       children.addAll(child._renameChildGroups(nameChange, displayNameChange, modifier, modifyTime, setAlternateName));
3231       children.addAll(child._renameChildStemsAndGroups(nameChange, displayNameChange, modifier, modifyTime, setAlternateName));
3232     }
3233     return children;
3234   } 
3235 
3236   /**
3237    * Rename attributeDef and attributeDefName due to a name and/or displayName change
3238    * @param nameChange
3239    * @param displayNameChange
3240    * @return the children
3241    */
3242   private Set<GrouperAPI> _renameAttr(boolean nameChange, boolean displayNameChange) {
3243     Set<GrouperAPI> children  = new LinkedHashSet();
3244 
3245     if (!nameChange && !displayNameChange) {
3246       return children;
3247     }
3248     
3249     Set<AttributeDefName> attributeDefNames = GrouperDAOFactory.getFactory().getAttributeDefName().findByStem(this.getUuid());
3250     Iterator<AttributeDefName> attributeDefNameIter = attributeDefNames.iterator();
3251     while (attributeDefNameIter.hasNext()) {
3252       AttributeDefName attributeDefName = attributeDefNameIter.next();
3253       
3254       if (nameChange) {
3255         attributeDefName.setNameDb(this.getName() + ":" + attributeDefName.getExtensionDb());
3256       }
3257       
3258       if (displayNameChange) {
3259         attributeDefName.setDisplayNameDb(this.getDisplayName() + ":" + attributeDefName.getDisplayExtensionDb());
3260       }
3261       
3262       children.add(attributeDefName);
3263     }
3264     
3265     if (nameChange) {
3266       Set<AttributeDef> attributeDefs = GrouperDAOFactory.getFactory().getAttributeDef().findByStem(this.getUuid());
3267       Iterator<AttributeDef> attributeDefIter = attributeDefs.iterator();
3268       while (attributeDefIter.hasNext()) {
3269         AttributeDef attributeDef = attributeDefIter.next();
3270         attributeDef.setNameDb(this.getName() + ":" + attributeDef.getExtensionDb());
3271         children.add(attributeDef);
3272       }
3273     }
3274     
3275     return children;
3276   }
3277 
3278 
3279   /**
3280    * revoke naming privs
3281    * @throws InsufficientPrivilegeException if problem
3282    * @throws RevokePrivilegeException if problem
3283    * @throws SchemaException if problem
3284    */
3285   private void _revokeAllNamingPrivs() 
3286     throws  InsufficientPrivilegeException,
3287             RevokePrivilegeException, 
3288             SchemaException {
3289 
3290     try {
3291       GrouperSession.callbackGrouperSession(
3292           GrouperSession.staticGrouperSession().internal_getRootSession(), 
3293           new GrouperSessionHandler() {
3294   
3295             public Object callback(GrouperSession grouperSession)
3296                 throws GrouperSessionException {
3297 
3298               try {
3299                 Stem.this.revokePriv(NamingPrivilege.CREATE);
3300                 Stem.this.revokePriv(NamingPrivilege.STEM_ADMIN);
3301                 Stem.this.revokePriv(NamingPrivilege.STEM_ATTR_READ);
3302                 Stem.this.revokePriv(NamingPrivilege.STEM_ATTR_UPDATE);
3303                 return null;
3304               } catch (InsufficientPrivilegeException ipe) {
3305                 throw new GrouperSessionException(ipe);
3306               } catch (RevokePrivilegeException rpe) {
3307                 throw new GrouperSessionException(rpe);
3308               } catch (SchemaException se) {
3309                 throw new GrouperSessionException(se);
3310               }
3311             }
3312         
3313       });
3314     } catch (GrouperSessionException gse) {
3315       if (gse.getCause() instanceof InsufficientPrivilegeException) {
3316         throw (InsufficientPrivilegeException) gse.getCause();
3317       }
3318       if (gse.getCause() instanceof RevokePrivilegeException) {
3319         throw (RevokePrivilegeException) gse.getCause();
3320       }
3321       if (gse.getCause() instanceof SchemaException) {
3322         throw (SchemaException) gse.getCause();
3323       }
3324       throw gse;
3325     }
3326   } // private void _revokeAllNamingPrivs()
3327 
3328   /**
3329    * 
3330    * @see java.lang.Object#equals(java.lang.Object)
3331    */
3332   public boolean equals(Object other) {
3333     if (this == other) {
3334       return true;
3335     }
3336     if (!(other instanceof Stem)) {
3337       return false;
3338     }
3339     return new EqualsBuilder()
3340       .append( this.name, ( (Stem) other ).name )
3341       .isEquals();
3342   } // public boolean equals(other)
3343 
3344   /**
3345    * 
3346    * @return create time
3347    */
3348   public long getCreateTimeLong() {
3349     return this.createTime;
3350   }
3351 
3352   /**
3353    * 
3354    * @return create time
3355    */
3356   public String getCreatorUuid() {
3357     return this.creatorUUID;
3358   }
3359 
3360   /**
3361    * @return description
3362    * @since   1.2.0
3363    */
3364   public String getDescriptionDb() {
3365     return this.description;
3366   }
3367 
3368   /**
3369    * @return displayExtension
3370    * @since   1.2.0
3371    */
3372   public String getDisplayExtensionDb() {
3373     return this.displayExtension;
3374   }
3375 
3376   /**
3377    * @return displayName
3378    * @since   1.2.0
3379    */
3380   public String getDisplayNameDb() {
3381     return this.displayName;
3382   }
3383 
3384   /**
3385    * @return extension
3386    * @since   1.2.0
3387    */
3388   public String getExtensionDb() {
3389     return this.extension;
3390   }
3391 
3392   /**
3393    * @return modifier uuid
3394    * @since   1.2.0
3395    */
3396   public String getModifierUuid() {
3397     return this.modifierUUID;
3398   }
3399 
3400   /**
3401    * @return modify time long
3402    * @since   1.2.0
3403    */
3404   public long getModifyTimeLong() {
3405     return this.modifyTime;
3406   }
3407 
3408   /**
3409    * @return name
3410    * @since   1.2.0
3411    */
3412   public String getNameDb() {
3413     return this.name;
3414   }
3415 
3416   /**
3417    * @return parent uuid
3418    * @since   1.2.0
3419    */
3420   public String getParentUuid() {
3421     return this.parentUuid;
3422   }
3423 
3424   /**
3425    * @return hash code
3426    * @since   1.2.0
3427    */
3428   public int hashCode() {
3429     return new HashCodeBuilder()
3430       .append( this.name )
3431       .toHashCode();
3432   } // public int hashCode()
3433 
3434   /**
3435    * @param createTime
3436    * @since   1.2.0
3437    */
3438   public void setCreateTimeLong(long createTime) {
3439     this.createTime = createTime;
3440   
3441   }
3442 
3443   /**
3444    * @param creatorUUID 
3445    * @since   1.2.0
3446    */
3447   public void setCreatorUuid(String creatorUUID) {
3448     this.creatorUUID = creatorUUID;
3449   
3450   }
3451 
3452   /**
3453    * @param description 
3454    * @since   1.2.0
3455    */
3456   public void setDescriptionDb(String description) {
3457     this.description = description;
3458   
3459   }
3460 
3461   /**
3462    * @param displayExtension 
3463    * @since   1.2.0
3464    */
3465   public void setDisplayExtensionDb(String displayExtension) {
3466     this.displayExtension = displayExtension;
3467   
3468   }
3469 
3470   /**
3471    * @param displayName 
3472    * @since   1.2.0
3473    */
3474   public void setDisplayName(String displayName) {
3475     this.displayName = displayName;
3476   
3477   }
3478 
3479   /**
3480    * @param displayName 
3481    * @since   1.2.0
3482    */
3483   public void setDisplayNameDb(String displayName) {
3484     this.displayName = displayName;
3485   
3486   }
3487 
3488   /**
3489    * @param extension 
3490    * @since   1.2.0
3491    */
3492   public void setExtensionDb(String extension) {
3493     this.extension = extension;
3494   
3495   }
3496 
3497   /**
3498    * @param modifierUUID 
3499    * @since   1.2.0
3500    */
3501   public void setModifierUuid(String modifierUUID) {
3502     this.modifierUUID = modifierUUID;
3503   
3504   }
3505 
3506   /**
3507    * @param modifyTime 
3508    * @since   1.2.0
3509    */
3510   public void setModifyTimeLong(long modifyTime) {
3511     this.modifyTime = modifyTime;
3512   
3513   }
3514 
3515   /**
3516    * @param name 
3517    * @since   1.2.0
3518    */
3519   public void setName(String name) {
3520     this.name = name;
3521   
3522   }
3523 
3524   /**
3525    * @param name 
3526    * @since   1.2.0
3527    */
3528   public void setNameDb(String name) {
3529     this.name = name;
3530   
3531   }
3532 
3533   /**
3534    * @param parentUUID 
3535    * @since   1.2.0
3536    */
3537   public void setParentUuid(String parentUUID) {
3538     this.parentUuid = parentUUID;
3539   
3540   }
3541 
3542   /**
3543    * @param uuid 
3544    * @since   1.2.0
3545    */
3546   public void setUuid(String uuid) {
3547     this.uuid = uuid;
3548   
3549   }
3550 
3551   /**
3552    * @return string
3553    * @since   1.2.0
3554    */
3555   public String toStringDb() {
3556     return new ToStringBuilder(this)
3557       .append( "createTime",       this.getCreateTime()       )
3558       .append( "creatorUuid",      this.getCreatorUuid()      )
3559       .append( "description",      this.getDescription()      )
3560       .append( "displayExtension", this.getDisplayExtension() )
3561       .append( "displayName",      this.getDisplayName()      )
3562       .append( "extension",        this.getExtension()        )
3563       .append( "modifierUuid",     this.getModifierUuid()     )
3564       .append( "modifyTime",       this.getModifyTime()       )
3565       .append( "name",             this.getName()             )
3566       .append( "ownerUuid",        this.getUuid()             )
3567       .append( "parentUuid",       this.getParentUuid()       )
3568       .toString();
3569   } // public String toString()
3570 
3571   /**
3572    * @see edu.internet2.middleware.grouper.GrouperAPI#onPostDelete(edu.internet2.middleware.grouper.hibernate.HibernateSession)
3573    */
3574   @Override
3575   public void onPostDelete(HibernateSession hibernateSession) {
3576 
3577     super.onPostDelete(hibernateSession);
3578     
3579     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.STEM, 
3580         StemHooks.METHOD_STEM_POST_COMMIT_DELETE, HooksStemBean.class, 
3581         this, Stem.class);
3582 
3583     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.STEM, 
3584         StemHooks.METHOD_STEM_POST_DELETE, HooksStemBean.class, 
3585         this, Stem.class, VetoTypeGrouper.STEM_POST_DELETE, false, true);
3586     
3587     InstrumentationThread.addCount(InstrumentationDataBuiltinTypes.API_STEM_DELETE.name());
3588   }
3589 
3590   /**
3591    * @see edu.internet2.middleware.grouper.GrouperAPI#onPostSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
3592    */
3593   @Override
3594   public void onPostSave(HibernateSession hibernateSession) {
3595 
3596     super.onPostSave(hibernateSession);
3597     
3598     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.STEM, 
3599         StemHooks.METHOD_STEM_POST_INSERT, HooksStemBean.class, 
3600         this, Stem.class, VetoTypeGrouper.STEM_POST_INSERT, true, false);
3601 
3602     //do these second so the right object version is set, and dbVersion is ok
3603     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.STEM, 
3604         StemHooks.METHOD_STEM_POST_COMMIT_INSERT, HooksStemBean.class, 
3605         this, Stem.class);
3606 
3607     InstrumentationThread.addCount(InstrumentationDataBuiltinTypes.API_STEM_ADD.name());
3608   }
3609 
3610   /**
3611    * @see edu.internet2.middleware.grouper.GrouperAPI#onPostUpdate(edu.internet2.middleware.grouper.hibernate.HibernateSession)
3612    */
3613   @Override
3614   public void onPostUpdate(HibernateSession hibernateSession) {
3615     
3616     if (this.dbVersionDifferentFields().contains(FIELD_NAME)) {
3617       GrouperSession.callbackGrouperSession(GrouperSession.staticGrouperSession().internal_getRootSession(), new GrouperSessionHandler() {
3618   
3619         /**
3620          * @see edu.internet2.middleware.grouper.misc.GrouperSessionHandler#callback(edu.internet2.middleware.grouper.GrouperSession)
3621          */
3622         public Object callback(GrouperSession rootSession) throws GrouperSessionException {
3623 
3624           // need to potentially update stem name in rules
3625           Set<RuleDefinition> definitions = RuleEngine.ruleEngine().getRuleDefinitions();
3626           for (RuleDefinition definition : definitions) {
3627             if (definition.getCheck() != null && definition.getCheck().checkTypeEnum() != null && 
3628                 definition.getCheck().checkTypeEnum().isCheckOwnerTypeStem(definition) && Stem.this.dbVersion().getName().equals(definition.getCheck().getCheckOwnerName())) {
3629               definition.getAttributeAssignType().getAttributeValueDelegate().assignValue(RuleUtils.ruleCheckOwnerNameName(), Stem.this.getName());
3630             }
3631             
3632             if (definition.getIfCondition() != null && definition.getIfCondition().ifConditionEnum() != null &&
3633                 definition.getIfCondition().ifConditionEnum().isIfOwnerTypeStem(definition) && Stem.this.dbVersion().getName().equals(definition.getIfCondition().getIfOwnerName())) {
3634               definition.getAttributeAssignType().getAttributeValueDelegate().assignValue(RuleUtils.ruleIfOwnerNameName(), Stem.this.getName());
3635             }
3636             
3637             // take care of nameMatchesSqlLikeString
3638             // if sql like string is a:b:%someGroup and a is changing to x:a, update sql like string to x:a:b:%someGroup
3639             if (definition.getIfCondition() != null && definition.getIfCondition().ifConditionEnum() == RuleIfConditionEnum.nameMatchesSqlLikeString) {
3640               if (definition.getIfCondition().getIfConditionEnumArg0().startsWith(Stem.this.dbVersion().getName() + ":")) {
3641                 String newString = Stem.this.getName() + definition.getIfCondition().getIfConditionEnumArg0().substring(Stem.this.dbVersion().getName().length());
3642                 definition.getAttributeAssignType().getAttributeValueDelegate().assignValue(RuleUtils.ruleIfConditionEnumArg0Name(), newString);
3643               }
3644             }
3645           }
3646           
3647           return null;
3648         }
3649       });
3650     }
3651     
3652     if (this.dbVersionDifferentFields().contains(FIELD_PARENT_UUID)) {
3653       // stem is being moved.  take care of stem sets..
3654       Set<StemSet> ifHasStemSetsOfParentStem = GrouperDAOFactory.getFactory().getStemSet().findByIfHasStemId(this.parentUuid);
3655       Set<StemSet> oldStemSets = GrouperDAOFactory.getFactory().getStemSet().findByIfHasStemId(this.uuid);
3656       GrouperDAOFactory.getFactory().getStem().moveStemSets(new LinkedList<StemSet>(ifHasStemSetsOfParentStem), new LinkedList<StemSet>(oldStemSets), this.uuid, 0);
3657     }
3658     
3659     super.onPostUpdate(hibernateSession);
3660 
3661     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.STEM, 
3662         StemHooks.METHOD_STEM_POST_COMMIT_UPDATE, HooksStemBean.class, 
3663         this, Stem.class);
3664 
3665     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.STEM, 
3666         StemHooks.METHOD_STEM_POST_UPDATE, HooksStemBean.class, 
3667         this, Stem.class, VetoTypeGrouper.STEM_POST_UPDATE, true, false);
3668   }
3669 
3670   /**
3671    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreDelete(edu.internet2.middleware.grouper.hibernate.HibernateSession)
3672    */
3673   @Override
3674   public void onPreDelete(HibernateSession hibernateSession) {
3675     super.onPreDelete(hibernateSession);
3676     
3677     //delete some caches
3678     Hib3MemberDAO.membersCacheClear();
3679     Hib3AttributeDefDAO.attributeDefCacheClear();
3680     Hib3AttributeDefNameDAO.attributeDefNameCacheClear();
3681     StemFinder.stemCacheClear();
3682     GroupFinder.groupCacheClear();
3683     Hib3AttributeAssignActionDAO.attributeAssignActionCacheClear();
3684     Hib3AttributeAssignDAO.attributeAssignCacheClear();
3685     
3686     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.STEM, 
3687         StemHooks.METHOD_STEM_PRE_DELETE, HooksStemBean.class, 
3688         this, Stem.class, VetoTypeGrouper.STEM_PRE_DELETE, false, false);
3689   
3690     //change log into temp table
3691     new ChangeLogEntry(true, ChangeLogTypeBuiltin.STEM_DELETE, 
3692         ChangeLogLabels.STEM_DELETE.id.name(), 
3693         this.getUuid(), ChangeLogLabels.STEM_DELETE.name.name(), 
3694         this.getName(), ChangeLogLabels.STEM_DELETE.parentStemId.name(), this.getParentUuid(),
3695         ChangeLogLabels.STEM_DELETE.displayName.name(), this.getDisplayName(),
3696         ChangeLogLabels.STEM_DELETE.description.name(), this.getDescription()).save();
3697   }
3698 
3699   /**
3700    * 
3701    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
3702    */
3703   @Override
3704   public void onPreSave(HibernateSession hibernateSession) {
3705     super.onPreSave(hibernateSession);
3706     
3707     if (this.idIndex == null) {
3708       this.idIndex = TableIndex.reserveId(TableIndexType.stem);
3709     }
3710 
3711     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.STEM, 
3712         StemHooks.METHOD_STEM_PRE_INSERT, HooksStemBean.class, 
3713         this, Stem.class, VetoTypeGrouper.STEM_PRE_INSERT, false, false);
3714   
3715     //change log into temp table
3716     new ChangeLogEntry(true, ChangeLogTypeBuiltin.STEM_ADD, 
3717         ChangeLogLabels.STEM_ADD.id.name(), 
3718         this.getUuid(), ChangeLogLabels.STEM_ADD.name.name(), 
3719         this.getName(), ChangeLogLabels.STEM_ADD.parentStemId.name(), this.getParentUuid(),
3720         ChangeLogLabels.STEM_ADD.displayName.name(), this.getDisplayName(),
3721         ChangeLogLabels.STEM_ADD.description.name(), this.getDescription()).save();
3722   }
3723 
3724   /** see if already in onPreUpdate, dont go in again */
3725   private static ThreadLocal<Boolean> inOnPreUpdate = new ThreadLocal<Boolean>();
3726   
3727   /** whether we should be setting alternate names for groups during moves and renames */
3728   private boolean setAlternateNameOnMovesAndRenames;
3729     
3730   /**
3731    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreUpdate(edu.internet2.middleware.grouper.hibernate.HibernateSession)
3732    */
3733   @Override
3734   public void onPreUpdate(HibernateSession hibernateSession) {
3735     super.onPreUpdate(hibernateSession);
3736     
3737     //delete some caches
3738     Hib3MemberDAO.membersCacheClear();
3739     Hib3AttributeDefDAO.attributeDefCacheClear();
3740     Hib3AttributeDefNameDAO.attributeDefNameCacheClear();
3741     StemFinder.stemCacheClear();
3742     GroupFinder.groupCacheClear();
3743     Hib3AttributeAssignActionDAO.attributeAssignActionCacheClear();
3744     Hib3AttributeAssignDAO.attributeAssignCacheClear();
3745 
3746     // If the stem name is changing, verify that the new name is not in use.
3747     // (The new name could be an alternate name).
3748     if (this.dbVersionDifferentFields().contains(FIELD_NAME)) {
3749       Stem check = GrouperDAOFactory.getFactory().getStem().findByName(this.getNameDb(), false);
3750       if (check != null && 
3751           (!check.getUuid().equals(this.getUuid()) || 
3752               (this.getAlternateNameDb() != null && 
3753                   this.getAlternateNameDb().equals(this.getNameDb())))) {
3754         throw new StemModifyException("Stem with name " + this.getNameDb() + " already exists.");
3755       }
3756     }
3757     
3758     // If the alternate name is changing, do the following check...
3759     // If the stem name is not changing OR
3760     // if the stem name is changing and the alternate name is not the old group name, THEN
3761     // we need to verify the alternate name isn't already taken.
3762     if (this.dbVersionDifferentFields().contains(FIELD_ALTERNATE_NAME_DB) &&
3763         this.getAlternateNameDb() != null) {
3764       
3765       String oldName = this.dbVersion().getNameDb();
3766       if (!this.dbVersionDifferentFields().contains(FIELD_NAME) || 
3767           (this.dbVersionDifferentFields().contains(FIELD_NAME) && 
3768               !oldName.equals(this.getAlternateNameDb()))) {
3769         Stem check = GrouperDAOFactory.getFactory().getStem().findByName(this.getAlternateNameDb(), false);
3770         if (check != null) {
3771           throw new StemModifyException("Stem with name " + this.getAlternateNameDb() + " already exists.");
3772         }
3773       }
3774     }
3775     
3776     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.STEM, 
3777         StemHooks.METHOD_STEM_PRE_UPDATE, HooksStemBean.class, 
3778         this, Stem.class, VetoTypeGrouper.STEM_PRE_UPDATE, false, false);
3779     
3780     //change log into temp table
3781     ChangeLogEntry.saveTempUpdates(ChangeLogTypeBuiltin.STEM_UPDATE, 
3782         this, this.dbVersion(),
3783         GrouperUtil.toList(ChangeLogLabels.STEM_UPDATE.id.name(),this.getUuid(), 
3784             ChangeLogLabels.STEM_UPDATE.name.name(), this.getName(),
3785             ChangeLogLabels.STEM_UPDATE.parentStemId.name(), this.getParentUuid(),
3786             ChangeLogLabels.STEM_UPDATE.displayName.name(), this.getDisplayName(),
3787             ChangeLogLabels.STEM_UPDATE.description.name(), this.getDescription()),
3788         GrouperUtil.toList(FIELD_NAME, FIELD_PARENT_UUID, FIELD_DESCRIPTION, FIELD_DISPLAY_EXTENSION),
3789         GrouperUtil.toList(ChangeLogLabels.STEM_UPDATE.name.name(),
3790             ChangeLogLabels.STEM_UPDATE.parentStemId.name(), 
3791             ChangeLogLabels.STEM_UPDATE.description.name(), 
3792             ChangeLogLabels.STEM_UPDATE.displayExtension.name()));
3793     
3794     //if supposed to not have setters do queries
3795     Boolean inOnPreUpdateBoolean = inOnPreUpdate.get();
3796     try {
3797       
3798       if (inOnPreUpdateBoolean == null || !inOnPreUpdateBoolean) {
3799         inOnPreUpdate.set(true);
3800         //check and see what needs to be updated
3801         boolean nameChange = false;
3802         boolean displayNameChange = false;
3803         
3804         Set<String> dbVersionDifferentFields = Stem.this.dbVersionDifferentFields();
3805         if (dbVersionDifferentFields.contains(FIELD_EXTENSION) || 
3806             dbVersionDifferentFields.contains(FIELD_NAME)) {
3807           nameChange = true;
3808         }
3809         if (dbVersionDifferentFields.contains(FIELD_DISPLAY_EXTENSION) ||
3810             dbVersionDifferentFields.contains(FIELD_DISPLAY_NAME)) {
3811           displayNameChange = true;
3812         }
3813         
3814         if (nameChange || displayNameChange) {
3815           // Now iterate through all child groups and stems, renaming each.
3816           GrouperDAOFactory.getFactory().getStem().renameStemAndChildren(
3817               Stem.this._renameChildren(nameChange, displayNameChange, Stem.this.setAlternateNameOnMovesAndRenames));
3818         }
3819         
3820         //if its description, just store, we are all good
3821       }
3822     } catch (StemModifyException ste) {
3823       //tunnel checked exceptions
3824       throw new RuntimeException(ste);
3825     } finally {
3826       //if we changed it
3827       if (inOnPreUpdateBoolean== null || !inOnPreUpdateBoolean) {
3828         //change it back
3829         inOnPreUpdate.remove();
3830       }
3831     }
3832   }
3833 
3834   /**
3835    * save the state when retrieving from DB
3836    * @return the dbVersion
3837    */
3838   @Override
3839   public Stem dbVersion() {
3840     return (Stem)this.dbVersion;
3841   }
3842 
3843   /**
3844    * note, these are massaged so that name, extension, etc look like normal fields.
3845    * access with fieldValue()
3846    * @see edu.internet2.middleware.grouper.GrouperAPI#dbVersionDifferentFields()
3847    */
3848   @Override
3849   public Set<String> dbVersionDifferentFields() {
3850     if (this.dbVersion == null) {
3851       throw new RuntimeException("State was never stored from db");
3852     }
3853     //easier to unit test if everything is ordered
3854     Set<String> result = GrouperUtil.compareObjectFields(this, this.dbVersion,
3855         DB_VERSION_FIELDS, null);
3856     return result;
3857   }
3858 
3859   /**
3860    * take a snapshot of the data since this is what is in the db
3861    */
3862   @Override
3863   public void dbVersionReset() {
3864     //lets get the state from the db so we know what has changed
3865     this.dbVersion = GrouperUtil.clone(this, DB_VERSION_FIELDS);
3866   }
3867 
3868   /**
3869    * deep clone the fields in this object
3870    */
3871   @Override
3872   public Stem clone() {
3873     return GrouperUtil.clone(this, CLONE_FIELDS);
3874   }
3875 
3876   /**
3877    * create stems and parents if not exist.
3878    * @param stemName
3879    * @param grouperSession 
3880    * @param stemDisplayNameForInserts optional, will use this for display name, and not just default to the name.  Note this is
3881    * only used if creating something, it will not update existing stems
3882    * @return the resulting stem
3883    * @throws InsufficientPrivilegeException 
3884    * @throws StemNotFoundException 
3885    * @throws StemAddException 
3886    */
3887   public static Stem _createStemAndParentStemsIfNotExist(final GrouperSession grouperSession, final String stemName, String stemDisplayNameForInserts)
3888      throws InsufficientPrivilegeException, StemNotFoundException, StemAddException {
3889     //note, no need for GrouperSession inverse of control
3890     String[] stems = StringUtils.split(stemName, ':');
3891     String[] displayStems = StringUtils.isBlank(stemDisplayNameForInserts) ? null : StringUtils.split(stemDisplayNameForInserts, ':');
3892 
3893     boolean hasDisplayStems = displayStems != null;
3894     
3895     if (hasDisplayStems) {
3896       if (stems.length != displayStems.length) {
3897         throw new RuntimeException("The length of stems in stem name: " + stems.length + ", " + stemName
3898             + ", should be the same as the display stems: " + displayStems.length + ", " + stemDisplayNameForInserts);
3899       }
3900     }
3901     
3902     Stem currentStem = StemFinder.findRootStem(grouperSession);
3903     String currentName = stems[0];
3904     for (int i=0;i<stems.length;i++) {
3905       try {
3906         currentStem = StemFinder.findByName(grouperSession, currentName, true, new QueryOptions().secondLevelCache(false));
3907       } catch (StemNotFoundException snfe1) {
3908         try {
3909           //this isnt ideal, but just use the extension as the display extension
3910           currentStem = currentStem.addChildStem(stems[i], hasDisplayStems ? displayStems[i] : stems[i], null, false);
3911         } catch (RuntimeException e) {
3912           for (int j=0; j<4; j++) {
3913             // could a tx need to finish?
3914             GrouperUtil.sleep(500);
3915             // see if another thread created it
3916             currentStem = StemFinder.findByName(grouperSession, currentName, false, new QueryOptions().secondLevelCache(false));
3917             if (currentName != null) {
3918               break;
3919             }
3920           }
3921           if (currentStem == null) {
3922             throw e;
3923           }
3924         }
3925       }
3926       //increment the name, dont worry if on the last one, we are done
3927       if (i < stems.length-1) {
3928         currentName += ":" + stems[i+1];
3929       }
3930     }
3931     Stem parentStem = null;
3932     //at this point the stem should be there (and is equal to currentStem), just to be sure, query again, do it in a loop
3933     //in case there are race conditions
3934     for (int i=0;i<20;i++) {
3935       
3936       //try with tx
3937       parentStem = StemFinder.findByName(grouperSession, stemName, false, new QueryOptions().secondLevelCache(false));
3938       
3939       if (parentStem != null) {
3940 
3941         return parentStem;
3942       }
3943 
3944       parentStem = (Stem)GrouperTransaction.callbackGrouperTransaction(GrouperTransactionType.NONE, new GrouperTransactionHandler() {
3945         
3946         public Object callback(GrouperTransaction grouperTransaction)
3947             throws GrouperDAOException {
3948           return StemFinder.findByName(grouperSession, stemName, false, new QueryOptions().secondLevelCache(false));
3949         }
3950       });
3951       
3952       if (parentStem != null) {
3953         // if its in another transaction... wait a couple seconds for it to finish
3954         GrouperUtil.sleep(2000);
3955         
3956         return parentStem;
3957       }
3958 
3959       GrouperUtil.sleep(1000);
3960     }
3961 
3962     throw new RuntimeException("Cant find stem after creating??? " + stemName);
3963 
3964   }
3965   
3966   /**
3967    * <pre>
3968    * create or update a stem.  Note this will not move a stem at this time (might in future)
3969    * 
3970    * This is a static method since setters to Stem objects persist to the DB
3971    * 
3972    * Steps:
3973    * 
3974    * 1. Find the stem by stemNameToEdit (if not there then its an insert)
3975    * 2. Internally set all the fields of the stem (no need to reset if already the same)
3976    * 3. Store the stem (insert or update) if needed
3977    * 4. Return the stem object
3978    * 
3979    * This occurs in a transaction, so if a part of it fails, it rolls back, and potentially
3980    * rolls back outer transactions too
3981    * </pre>
3982    * @param grouperSession to act as
3983    * @param stemNameToEdit is the name of the stem to edit (or null if insert)
3984    * @param description new description for stem
3985    * @param displayExtension display friendly name for this stem only
3986    * (parent stems are not specified)
3987    * @param name this is required, and is the full name of the stem
3988    * including the names of parent stems.  e.g. stem1:stem2:stem3
3989    * the parent stem must exist unless createParentStemsIfNotExist.  
3990    * Can rename a stem extension, but not the parent stem name (move)
3991    * @param uuid of the stem.  uuid for an inserted stem
3992    * @param saveMode to constrain if insert only or update only, if null defaults to INSERT_OR_UPDATE
3993    * @param createParentStemsIfNotExist true if the stems should be created if they dont exist, false
3994    * for StemNotFoundException if not exist.  Note, the display extension on created stems
3995    * will equal the extension.  This could be dangerous and should probably only be used for testing
3996    * @return the stem that was updated or created
3997    * @throws StemNotFoundException 
3998    * @throws InsufficientPrivilegeException 
3999    * @throws StemAddException 
4000    * @throws StemModifyException 
4001    */
4002   public static Stem saveStem(final GrouperSession grouperSession, final String stemNameToEdit,
4003       final String uuid, final String name, final String displayExtension, final String description, 
4004       SaveMode saveMode, final boolean createParentStemsIfNotExist) 
4005         throws StemNotFoundException,
4006       InsufficientPrivilegeException,
4007       StemAddException, StemModifyException {
4008   
4009     StemSave/StemSave.html#StemSave">StemSave stemSave = new StemSave(grouperSession);
4010 
4011     stemSave.assignStemNameToEdit(stemNameToEdit).assignUuid(uuid);
4012     stemSave.assignName(name).assignDisplayExtension(displayExtension);
4013     stemSave.assignDescription(description).assignSaveMode(saveMode);
4014     stemSave.assignCreateParentStemsIfNotExist(createParentStemsIfNotExist);
4015     Stem stem = stemSave.save();
4016 
4017     return stem;
4018 
4019   }
4020 
4021   /**
4022    * Move this stem to another Stem.  If you would like to specify options for the move, 
4023    * use StemMove instead.  This will use the default options.
4024    * @param stem 
4025    * @throws StemModifyException 
4026    * @throws InsufficientPrivilegeException 
4027    */
4028   public void move(Stem stem) throws StemModifyException,
4029       InsufficientPrivilegeException {
4030     
4031     new StemMove(this, stem).save();
4032   }
4033   
4034   /**
4035    * 
4036    * @param stem
4037    * @param assignAlternateName
4038    * @throws StemModifyException
4039    * @throws InsufficientPrivilegeException
4040    */
4041   protected void internal_move(final Stem stem, final boolean assignAlternateName) throws StemModifyException,
4042     InsufficientPrivilegeException {
4043 
4044     HibernateSession.callbackHibernateSession(
4045         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
4046         new HibernateHandler() {
4047 
4048           public Object callback(HibernateHandlerBean hibernateHandlerBean)
4049               throws GrouperDAOException {
4050 
4051             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
4052 
4053             GrouperSession.validate(GrouperSession.staticGrouperSession());
4054             
4055             String oldName = Stem.this.getName();
4056             
4057             // cannot move the root stem
4058             if (Stem.this.isRootStem()) {
4059               throw new StemModifyException("Cannot move the root stem.");
4060             }
4061             
4062             // the new stem should not be a child of the current stem
4063             if (stem.getUuid().equals(Stem.this.getUuid()) || 
4064                 Stem.this.isChildStem(stem)) {
4065               throw new StemModifyException("Cannot move stem. " + stem.getName()
4066                   + " is the same as or a child of " + Stem.this.getName() + ".");
4067             }
4068 
4069             // verify that the subject has stem privileges to the stem
4070             if (!PrivilegeHelper.canStemAdmin(Stem.this, GrouperSession.staticGrouperSession()
4071                 .getSubject())) {
4072               throw new InsufficientPrivilegeException(E.CANNOT_STEM_ADMIN + ": " + Stem.this.getName());
4073             }
4074 
4075             // verify that the subject can create stems in the stem where this stem will be moved to.
4076             if (!PrivilegeHelper.canCreate(GrouperSession.staticGrouperSession(), stem, GrouperSession.staticGrouperSession()
4077                 .getSubject())) {
4078               throw new InsufficientPrivilegeException(E.CANNOT_CREATE + ": " + stem.getName());
4079             }
4080             
4081             // verify that if the property security.stem.groupAllowedToMoveStem is set,
4082             // then the user is a member of that group.
4083             if (!PrivilegeHelper.canMoveStems(GrouperSession.staticGrouperSession().getSubject())) {
4084               throw new InsufficientPrivilegeException("User cannot move stems.");      
4085             }
4086             
4087             // if moving to the same stem, just return.
4088             if (stem.getUuid().equals(Stem.this.getParentUuid())) {
4089               return null;
4090             }
4091 
4092             Stem.this.setParentUuid(stem.getUuid());
4093             Stem.this.internal_setModified();
4094             
4095             if (stem.isRootStem()) {
4096               Stem.this.setNameDb(Stem.this.getExtension());
4097               Stem.this.setDisplayNameDb(Stem.this.getDisplayExtension());
4098             } else {
4099               Stem.this.setNameDb(stem.getName() + Stem.DELIM + Stem.this.getExtension());
4100               Stem.this.setDisplayNameDb(stem.getDisplayName() + Stem.DELIM
4101                   + Stem.this.getDisplayExtension());
4102             }
4103             
4104             if (assignAlternateName) {
4105               Stem.this.internal_addAlternateName(oldName);
4106             }
4107             
4108             Stem.this.setAlternateNameOnMovesAndRenames = assignAlternateName;
4109             
4110             Stem.this.store();
4111             
4112             //if not a smaller operation of a larger auditable call
4113             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
4114 
4115               AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.STEM_MOVE,
4116                   "stemId", Stem.this.getUuid(), "oldStemName", 
4117                   oldName, "newStemName", Stem.this.getName(), "newParentStemId",
4118                   stem.getUuid(), 
4119                   "assignAlternateName", assignAlternateName ? "T" : "F");
4120               auditEntry.setDescription("Move stem " + oldName + " to name: " + Stem.this.getName()
4121                   + ", assignAlternateName? " + (assignAlternateName ? "T" : "F")); 
4122               auditEntry.saveOrUpdate(true);
4123             }
4124 
4125             
4126             return null;
4127           }
4128         });
4129 
4130     
4131   }
4132   
4133   
4134   /**
4135    * Copy this stem to another Stem.
4136    * @param stem 
4137    * @param privilegesOfStem Whether to copy privileges of stems
4138    * @param privilegesOfGroup Whether to copy privileges of groups
4139    * @param groupAsPrivilege Whether to copy privileges where groups are a member
4140    * @param listMembersOfGroup Whether to copy the list memberships of groups
4141    * @param listGroupAsMember Whether to copy list memberships where groups are a member
4142    * @param attributes Whether to copy attributes
4143    * @return the new stem
4144    * @throws StemAddException 
4145    * @throws InsufficientPrivilegeException 
4146    *
4147    */
4148   protected StemStem.html#Stem">Stem internal_copy(final Stem stem, final boolean privilegesOfStem,
4149       final boolean privilegesOfGroup, final boolean groupAsPrivilege,
4150       final boolean listMembersOfGroup, final boolean listGroupAsMember,
4151       final boolean attributes) throws StemAddException, InsufficientPrivilegeException {
4152 
4153     return (Stem) HibernateSession.callbackHibernateSession(
4154         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
4155         new HibernateHandler() {
4156 
4157           public Object callback(HibernateHandlerBean hibernateHandlerBean)
4158               throws GrouperDAOException {
4159 
4160             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
4161             
4162             // cannot copy the root stem
4163             if (Stem.this.isRootStem()) {
4164               throw new StemAddException("Cannot copy the root stem.");
4165             }
4166             
4167             // the new stem should not be a child of the current stem
4168             if (stem.getUuid().equals(Stem.this.getUuid()) || 
4169                 Stem.this.isChildStem(stem)) {
4170               throw new StemAddException("Cannot copy stem. " + stem.getName()
4171                   + " is the same as or a child of " + Stem.this.getName() + ".");
4172             }
4173 
4174             // verify that if the property security.stem.groupAllowedToCopyStem is set,
4175             // then the user is a member of that group.
4176             if (!PrivilegeHelper.canCopyStems(GrouperSession.staticGrouperSession().getSubject())) {
4177               throw new InsufficientPrivilegeException("User cannot copy stems.");      
4178             }
4179             
4180             if (!PrivilegeHelper.canStem(Stem.this, GrouperSession.staticGrouperSession().getSubject())) {
4181               throw new InsufficientPrivilegeException("Cannot copy stem: " + Stem.this.getName());      
4182             }
4183             
4184             Map<String, Stem> oldStemUuidToNewStem = new HashMap<String, Stem>();
4185             Map<String, Group> oldGroupUuidToNewGroup = new HashMap<String, Group>();
4186             Set<Composite> oldComposites = new LinkedHashSet<Composite>();
4187             
4188             // now lets copy over the stems
4189             Stem newStem = stem.internal_addChildStem(GrouperSession
4190                 .staticGrouperSession(), Stem.this.getExtension(),
4191                 Stem.this.getDisplayExtension(), null, false, true);
4192             
4193             if (privilegesOfStem) {
4194               newStem.internal_copyPrivilegesOfStem(GrouperSession
4195                   .staticGrouperSession().internal_getRootSession(), Stem.this);
4196             }
4197             
4198             oldStemUuidToNewStem.put(Stem.this.getUuid(), newStem);
4199             
4200             for (Stem childStem : GrouperDAOFactory.getFactory().getStem()
4201                     .findAllChildStems(Stem.this, Stem.Scope.SUB, new QueryOptions().sortAsc("name"), false)) {
4202               Stem newChildStem = oldStemUuidToNewStem.get(childStem.getParentUuid())
4203                   .internal_addChildStem(GrouperSession
4204                       .staticGrouperSession().internal_getRootSession(), childStem.getExtension(),
4205                       childStem.getDisplayExtension(), null, false, true);
4206               
4207               if (privilegesOfStem) {
4208                 newChildStem.internal_copyPrivilegesOfStem(GrouperSession
4209                     .staticGrouperSession().internal_getRootSession(), childStem);
4210               }
4211               
4212               oldStemUuidToNewStem.put(childStem.getUuid(), newChildStem);
4213             }
4214             
4215             // now lets copy over the groups
4216             for (Group child : GrouperDAOFactory.getFactory().getStem()
4217                 .findAllChildGroups(Stem.this, Stem.Scope.SUB)) {
4218               Group newChild = child.internal_copy(oldStemUuidToNewStem.get(child
4219                   .getParentUuid()), privilegesOfGroup, groupAsPrivilege,
4220                   listMembersOfGroup, listGroupAsMember, attributes, false, false, false, null, null);
4221               oldGroupUuidToNewGroup.put(child.getUuid(), newChild);
4222               
4223               Composite oldComposite = GrouperDAOFactory.getFactory().getComposite()
4224                   .findAsOwner(child, false);
4225               if (oldComposite != null) {
4226                 
4227                 oldComposites.add(oldComposite);
4228               }
4229             }
4230       
4231             
4232             // need to take care of composites....
4233             Iterator<Composite> oldCompositesIter = oldComposites.iterator();
4234             while (oldCompositesIter.hasNext()) {
4235               Composite oldComposite = oldCompositesIter.next();
4236               String oldOwnerUuid = oldComposite.getFactorOwnerUuid();
4237               String oldLeftUuid = oldComposite.getLeftFactorUuid();
4238               String oldRightUuid = oldComposite.getRightFactorUuid();
4239 
4240               Group newCompositeOwnerGroup = oldGroupUuidToNewGroup.get(oldOwnerUuid);
4241               Group newCompositeLeftGroup = oldGroupUuidToNewGroup.get(oldLeftUuid);
4242               Group newCompositeRightGroup = oldGroupUuidToNewGroup.get(oldRightUuid);
4243 
4244               // if the factors aren't part of the stem being moved, 
4245               // we'll create the composite using the old factors...
4246               // maybe the way this works should be configurable? 
4247               if (newCompositeLeftGroup == null || newCompositeRightGroup == null) {
4248                 try {
4249                   newCompositeLeftGroup = oldComposite.getLeftGroup();
4250                   newCompositeRightGroup = oldComposite.getRightGroup();
4251                 } catch (GroupNotFoundException gnfe) {
4252                   if (LOG.isDebugEnabled()) {
4253                     LOG.debug("Not allowed to see composite factor of owner in stem copy: " + newCompositeOwnerGroup.getName() );
4254                   }
4255                   //not allowed to view left or right?  skip it
4256                   continue;
4257                 }
4258               }
4259 
4260               newCompositeOwnerGroup.internal_addCompositeMember(GrouperSession
4261                   .staticGrouperSession().internal_getRootSession(), oldComposite
4262                   .getType(), newCompositeLeftGroup, newCompositeRightGroup, null);
4263             }
4264 
4265             //if not a smaller operation of a larger auditable call
4266             if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
4267               
4268               AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.STEM_COPY,
4269                   "oldStemId", Stem.this.getUuid(), "oldStemName", 
4270                   Stem.this.getName(), "newStemName", newStem.getName(), "newStemId",
4271                   newStem.getUuid(), 
4272                   "privilegesOfStem", privilegesOfStem ? "T" : "F", "privilegesOfGroup",
4273                       privilegesOfGroup ? "T" : "F", "listMembersOfGroup",
4274                   listMembersOfGroup ? "T" : "F", "listGroupAsMember",
4275                   listGroupAsMember ? "T" : "F");
4276               auditEntry.setInt01(attributes ? 1L : 0L);
4277               auditEntry.setDescription("Copy stem " 
4278                   + Stem.this.getName() + " to name: " + newStem.getName()
4279                   + ", privilegesOfStem? " + (privilegesOfStem ? "T" : "F")
4280                   + ", privilegesOfGroup? " + (privilegesOfGroup ? "T" : "F")
4281                   + ", groupAsPrivilege? " + (groupAsPrivilege ? "T" : "F") 
4282                   + ", listMembersOfGroup? " + (listMembersOfGroup ? "T" : "F") 
4283                   + ", listGroupAsMember? " + (listGroupAsMember ? "T" : "F") 
4284                   + ", attributes? " + (attributes ? "T" : "F")); 
4285               auditEntry.saveOrUpdate(true);
4286             }
4287 
4288             
4289             return newStem;
4290           }
4291         });
4292   }
4293 
4294 
4295   /**
4296    * Copy this stem to another Stem.  If you want to specify options
4297    * for the copy, use StemCopy.  This will use the default options.
4298    * @param stem
4299    * @return the new stem
4300    * @throws StemAddException 
4301    * @throws InsufficientPrivilegeException 
4302    */
4303   public Stem href="../../../../edu/internet2/middleware/grouper/Stem.html#Stem">Stem copy(Stem stem) throws StemAddException, InsufficientPrivilegeException {
4304     StemCopy/StemCopy.html#StemCopy">StemCopy stemCopy = new StemCopy(this, stem);
4305     return stemCopy.save();  
4306   } 
4307   
4308   /**
4309    * 
4310    * @param session
4311    * @param stem
4312    * @throws UnableToPerformException
4313    */
4314   private void internal_copyPrivilegesOfStem(GrouperSession session, Stem stem)
4315       throws UnableToPerformException {
4316     Set<Privilege> privileges = Privilege.getNamingPrivs();
4317   
4318     Iterator<Privilege> iter = privileges.iterator();
4319     while (iter.hasNext()) {
4320       Privilege priv = iter.next();
4321       session.getNamingResolver().privilegeCopy(stem, this, priv);      
4322     }  
4323   }
4324   
4325   /**
4326    * when the last member has changed, used by hibernate
4327    */
4328   private Long lastMembershipChangeDb;
4329 
4330   /** id of the group as a unique integer */
4331   private Long idIndex;
4332   
4333   /**
4334    * when the last member has changed, used by hibernate
4335    * @return when
4336    */
4337   public Long getLastMembershipChangeDb() {
4338     return this.lastMembershipChangeDb;
4339   }
4340   
4341   /**
4342    * when the last member has changed, used by hibernate
4343    * @param theMembershipLastChange
4344    */
4345   public void setLastMembershipChangeDb(Long theMembershipLastChange) {
4346     this.lastMembershipChangeDb = theMembershipLastChange;
4347   }
4348   
4349   /**
4350    * when the last member has changed
4351    * @return the membership last change timestamp
4352    */
4353   public Timestamp getLastMembershipChange() {
4354     return this.lastMembershipChangeDb == null ? null : new Timestamp(this.lastMembershipChangeDb);
4355   }
4356 
4357   // PUBLIC INSTANCE METHODS //
4358   
4359   // PUBLIC INSTANCE METHODS //
4360   
4361   /**
4362    * Add a new role to the registry.
4363    * <pre class="eg">
4364    * // Add a role with the extension "edu" beneath this stem.
4365    * try {
4366    *   Group edu = ns.addChildRole("edu", "edu domain");
4367    * }
4368    * catch (GroupAddException eGA) {
4369    *   // Group not added
4370    * }
4371    * catch (InsufficientPrivilegeException eIP) {
4372    *   // Not privileged to add group
4373    * }
4374    * </pre>
4375    * @param   extension         Role extension
4376    * @param   displayExtension  Role displayExtension
4377    * @return  The added {@link Role}
4378    * @throws  GroupAddException 
4379    * @throws  InsufficientPrivilegeException
4380    */
4381   public Role addChildRole(final String extension, final String displayExtension) 
4382     throws  GroupAddException,
4383             InsufficientPrivilegeException  {
4384     return internal_addChildRole(extension, displayExtension, null);
4385   }
4386 
4387   /**
4388    * Add a new role to the registry.
4389    * <pre class="eg">
4390    * // Add a role with the extension "edu" beneath this stem.
4391    * try {
4392    *   Group edu = ns.addChildRole("edu", "edu domain");
4393    * }
4394    * catch (GroupAddException eGA) {
4395    *   // Group not added
4396    * }
4397    * catch (InsufficientPrivilegeException eIP) {
4398    *   // Not privileged to add group
4399    * }
4400    * </pre>
4401    * @param   extension         Role extension
4402    * @param   displayExtension  Role displayExtension
4403    * @param uuid is uuid or null if generated
4404    * @return  The added {@link Role}
4405    * @throws  GroupAddException 
4406    * @throws  InsufficientPrivilegeException
4407    */
4408   public Role internal_addChildRole(final String extension, final String displayExtension, final String uuid) 
4409     throws  GroupAddException,
4410             InsufficientPrivilegeException  {
4411     
4412     return (Group)HibernateSession.callbackHibernateSession(GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_NOT_AUDIT, new HibernateHandler() {
4413       public Object callback(HibernateHandlerBean hibernateHandlerBean)
4414           throws GrouperDAOException {
4415 
4416         hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
4417 
4418         //note, roles are modeled as groups
4419         Group group = Stem.this.internal_addChildGroup(extension, displayExtension, uuid, TypeOfGroup.role);
4420         
4421         RoleSeter/permissions/role/RoleSet.html#RoleSet">RoleSet roleSet = new RoleSet();
4422         roleSet.setId(StringUtils.isBlank(uuid) ? GrouperUuid.getUuid() : uuid);
4423         roleSet.setDepth(0);
4424         roleSet.setIfHasRoleId(group.getId());
4425         roleSet.setThenHasRoleId(group.getId());
4426         roleSet.setType(RoleHierarchyType.self);
4427         roleSet.setParentRoleSetId(roleSet.getId());
4428         roleSet.saveOrUpdate();
4429         
4430         return group;
4431       }
4432       
4433     });
4434       
4435   }
4436 
4437   /**
4438    * Add a new role to the registry.
4439    * <pre class="eg">
4440    * // Add a role with the extension "edu" beneath this stem.
4441    * try {
4442    *   Group edu = ns.addChildEntity("edu", "edu domain");
4443    * }
4444    * catch (GroupAddException eGA) {
4445    *   // Group not added
4446    * }
4447    * catch (InsufficientPrivilegeException eIP) {
4448    *   // Not privileged to add group
4449    * }
4450    * </pre>
4451    * @param   extension         Entity extension
4452    * @param   displayExtension  Entity displayExtension
4453    * @param uuid is uuid or null if generated
4454    * @return  The added {@link Role}
4455    * @throws  GroupAddException 
4456    * @throws  InsufficientPrivilegeException
4457    */
4458   public Role internal_addChildEntity(final String extension, final String displayExtension, final String uuid) 
4459     throws  GroupAddException,
4460             InsufficientPrivilegeException  {
4461     
4462     return (Group)HibernateSession.callbackHibernateSession(GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_NOT_AUDIT, new HibernateHandler() {
4463       public Object callback(HibernateHandlerBean hibernateHandlerBean)
4464           throws GrouperDAOException {
4465 
4466         hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
4467 
4468         //note, roles are modeled as groups
4469         Group group = Stem.this.internal_addChildGroup(extension, displayExtension, uuid, TypeOfGroup.entity);
4470         
4471         return group;
4472       }
4473       
4474     });
4475       
4476   }
4477 
4478 
4479   /**
4480    * 
4481    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlDifferentBusinessProperties(java.lang.Object)
4482    */
4483   public boolean xmlDifferentBusinessProperties(Stem other) {
4484     
4485     if (!StringUtils.equals(this.alternateNameDb, other.alternateNameDb)) {
4486       return true;
4487     }
4488     if (!StringUtils.equals(StringUtils.trimToNull(this.description), StringUtils.trimToNull(other.description))) {
4489       return true;
4490     }
4491     if (!StringUtils.equals(this.displayExtension, other.displayExtension)) {
4492       return true;
4493     }
4494     if (!StringUtils.equals(this.displayName, other.displayName)) {
4495       return true;
4496     }
4497     if (!StringUtils.equals(this.extension, other.extension)) {
4498       return true;
4499     }
4500     if (!StringUtils.equals(this.name, other.name)) {
4501       return true;
4502     }
4503     if (!StringUtils.equals(this.parentUuid, other.parentUuid)) {
4504       return true;
4505     }
4506     if (!StringUtils.equals(this.uuid, other.uuid)) {
4507       return true;
4508     }
4509 
4510     return false;
4511   }
4512 
4513   /**
4514    * 
4515    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlDifferentUpdateProperties(java.lang.Object)
4516    */
4517   public boolean xmlDifferentUpdateProperties(Stem other) {
4518     if (!StringUtils.equals(this.contextId, other.contextId)) {
4519       return true;
4520     }
4521     if (this.createTime != other.createTime) {
4522       return true;
4523     }
4524     if (!StringUtils.equals(this.creatorUUID, other.creatorUUID)) {
4525       return true;
4526     }
4527     if (!GrouperUtil.equals(this.getHibernateVersionNumber(), other.getHibernateVersionNumber())) {
4528       return true;
4529     }
4530     if (!GrouperUtil.equals(this.lastMembershipChangeDb, other.lastMembershipChangeDb)) {
4531       return true;
4532     }
4533     if (!StringUtils.equals(this.modifierUUID, other.modifierUUID)) {
4534       return true;
4535     }
4536     if (this.modifyTime != other.modifyTime) {
4537       return true;
4538     }
4539     return false;
4540 
4541   }
4542 
4543   /**
4544    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlCopyBusinessPropertiesToExisting(java.lang.Object)
4545    */
4546   public void xmlCopyBusinessPropertiesToExisting(Stem existingRecord) {
4547     existingRecord.setAlternateNameDb(this.alternateNameDb);
4548     existingRecord.setDescriptionDb(this.getDescriptionDb());
4549     existingRecord.setDisplayExtensionDb(this.getDisplayExtensionDb());
4550     existingRecord.setDisplayNameDb(this.getDisplayNameDb());
4551     existingRecord.setExtensionDb(this.getExtensionDb());
4552     existingRecord.setNameDb(this.getNameDb());
4553     existingRecord.setParentUuid(this.getParentUuid());
4554     existingRecord.setUuid(this.getUuid());
4555   }
4556 
4557 
4558 
4559   /**
4560    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlRetrieveByIdOrKey()
4561    */
4562   public Stem xmlRetrieveByIdOrKey() {
4563     return GrouperDAOFactory.getFactory().getStem().findByUuidOrName(this.uuid, this.name, false,
4564         new QueryOptions().secondLevelCache(false));
4565   }
4566 
4567   /**
4568    * assign different id index
4569    * @param theIdIndex
4570    * @return if it was changed
4571    */
4572   public boolean assignIdIndex(final long theIdIndex) {
4573     
4574     TableIndex.assertCanAssignIdIndex();
4575 
4576     boolean needsSave = false;
4577     
4578     synchronized (TableIndexType.stem) {
4579 
4580       //ok, if the index is not in use (not, it could be reserved... hmmm)
4581       Stem tempStem = GrouperDAOFactory.getFactory().getStem().findByIdIndex(theIdIndex, false, null);
4582       if (tempStem == null) {
4583         
4584         this.setIdIndex(theIdIndex);
4585         TableIndex.clearReservedId(TableIndexType.stem, theIdIndex);
4586         needsSave = true;
4587         
4588         //do a new session so we don hold on too long
4589         HibernateSession.callbackHibernateSession(GrouperTransactionType.READ_WRITE_NEW, AuditControl.WILL_NOT_AUDIT, new HibernateHandler() {
4590           
4591           @Override
4592           public Object callback(HibernateHandlerBean hibernateHandlerBean)
4593               throws GrouperDAOException {
4594             //now we might need to increment the index
4595             TableIndex tableIndex = GrouperDAOFactory.getFactory().getTableIndex().findByType(TableIndexType.stem);
4596             if (tableIndex != null && tableIndex.getLastIndexReserved() < theIdIndex) {
4597               tableIndex.setLastIndexReserved(theIdIndex);
4598               tableIndex.saveOrUpdate();
4599             }
4600             return null;
4601           }
4602         });
4603       }      
4604     }
4605     return needsSave;
4606   }
4607 
4608 
4609   /**
4610    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlSaveBusinessProperties(java.lang.Object)
4611    */
4612   public Stemu/internet2/middleware/grouper/Stem.html#Stem">Stem xmlSaveBusinessProperties(Stem existingRecord) {
4613 
4614     //if its an insert, call the business method
4615     if (existingRecord == null) {
4616       if (this.isRootStem()) {
4617         throw new RuntimeException("Why is there no root stem???");
4618       }
4619       Stem parent = this.getParentStem();
4620       existingRecord = parent.internal_addChildStem(GrouperSession.staticGrouperSession(), this.extension, this.displayExtension, this.uuid, false, true);
4621 
4622       if (this.idIndex != null) {
4623         existingRecord.assignIdIndex(this.idIndex);
4624       }
4625     }
4626     this.xmlCopyBusinessPropertiesToExisting(existingRecord);
4627     //if its an insert or update, then do the rest of the fields
4628     existingRecord.store();
4629     return existingRecord;
4630   }
4631 
4632 
4633   /**
4634    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlSaveUpdateProperties()
4635    */
4636   public void xmlSaveUpdateProperties() {
4637     
4638     GrouperDAOFactory.getFactory().getStem().saveUpdateProperties(this);
4639     
4640   }
4641 
4642   /**
4643    * convert to xml bean for export
4644    * @param grouperVersion
4645    * @return xml bean
4646    */
4647   public XmlExportStem xmlToExportStem(GrouperVersion grouperVersion) {
4648     if (grouperVersion == null) {
4649       throw new RuntimeException();
4650     }
4651 
4652     XmlExportStemt/XmlExportStem.html#XmlExportStem">XmlExportStem xmlExportStem = new XmlExportStem();
4653     
4654     xmlExportStem.setAlternateName(this.getAlternateNameDb());
4655     xmlExportStem.setContextId(this.getContextId());
4656     xmlExportStem.setCreateTime(GrouperUtil.dateStringValue(this.getCreateTime()));
4657     xmlExportStem.setCreatorId(this.getCreatorUuid());
4658     xmlExportStem.setDescription(this.getDescriptionDb());
4659     xmlExportStem.setDisplayExtension(this.getDisplayExtensionDb());
4660     xmlExportStem.setDisplayName(this.getDisplayNameDb());
4661     xmlExportStem.setExtension(this.getExtensionDb());
4662     xmlExportStem.setHibernateVersionNumber(this.getHibernateVersionNumber());
4663     xmlExportStem.setIdIndex(this.getIdIndex());
4664     //TODO make string
4665     xmlExportStem.setLastMembershipChange(this.getLastMembershipChangeDb());
4666     xmlExportStem.setModifierId(this.getModifierUuid());
4667     xmlExportStem.setModifierTime(GrouperUtil.dateStringValue(this.getModifyTime()));
4668     xmlExportStem.setName(this.getNameDb());
4669     xmlExportStem.setParentStem(this.getParentUuid());
4670     xmlExportStem.setUuid(this.getUuid());
4671     return xmlExportStem;
4672   }
4673 
4674   /**
4675    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlGetId()
4676    */
4677   public String xmlGetId() {
4678     return this.getUuid();
4679   }
4680 
4681   /**
4682    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlSetId(java.lang.String)
4683    */
4684   public void xmlSetId(String theId) {
4685     this.setUuid(theId);
4686   }
4687 
4688 
4689   /**
4690    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlToString()
4691    */
4692   public String xmlToString() {
4693     return "Stem: " + this.uuid + ", " + this.name;
4694   }
4695 
4696   /**
4697    * obliterate this stem name (which might not exist) from point in time
4698    * @param stemName
4699    * @param printOutput
4700    */
4701   public static void obliterateFromPointInTime(String stemName, final boolean printOutput) {
4702     Stem ns = StemFinder.findByName(GrouperSession.staticGrouperSession(), stemName, false);
4703     obliterateFromPointInTimeHelper(stemName, ns, printOutput);
4704   }
4705 
4706   /**
4707    * obliterate this stem name (which might not exist) from point in time
4708    * @param stemName
4709    * @param printOutput
4710    * @param testOnly 
4711    * @param deletePointInTime 
4712    */
4713   public static void obliterate(String stemName,final boolean printOutput, boolean testOnly, boolean deletePointInTime) {
4714     Stem ns = StemFinder.findByName(GrouperSession.staticGrouperSession(), stemName, false);
4715     if (ns != null) {
4716       ns.obliterate(printOutput, testOnly);
4717     }
4718     if (!testOnly && deletePointInTime) {
4719       obliterateFromPointInTimeHelper(stemName, ns, printOutput);
4720     }
4721   }
4722 
4723   /**
4724    * obliterate this stem name (which might not exist) from point in time
4725    * @param stemName
4726    * @param printOutput
4727    * @param ns
4728    */
4729   private static void obliterateFromPointInTimeHelper(String stemName,Stem ns,final boolean printOutput) {
4730 
4731     //make sure something in changelog temp
4732     if (HibernateSession.bySqlStatic().select(int.class, "SELECT count(1) FROM grouper_change_log_entry_temp") > 0) {
4733       
4734       //see if we are making progress
4735       long maxCreatedOn = HibernateSession.bySqlStatic().select(long.class, "SELECT max(created_on) FROM grouper_change_log_entry_temp");
4736       int changeLogEntryCount = HibernateSession.bySqlStatic().select(int.class, "SELECT count(1) FROM grouper_change_log_entry_temp where created_on <= ?", 
4737           GrouperUtil.toListObject(maxCreatedOn), HibUtils.listType(LongType.INSTANCE));
4738 
4739       int loops = 0;
4740       
4741       OUTER:
4742       while (true) {
4743         if (ns != null) {
4744           PITStem pitStem = GrouperDAOFactory.getFactory().getPITStem().findBySourceIdUnique(ns.getUuid(), false);
4745           if (pitStem != null && !pitStem.isActive()) {
4746             break;
4747           }
4748         } else {
4749           Set<PITStem> pitStems = GrouperDAOFactory.getFactory().getPITStem().findByName(stemName, false);
4750           if (pitStems.size() > 0 && !pitStems.iterator().next().isActive()) {
4751             break;
4752           } 
4753         }
4754         
4755         if (printOutput) {
4756           System.out.println("Waiting for Grouper Daemon to process before obliterating from point in time data. "
4757               + " This is expected to take a few minutes.  Be sure the Grouper Daemon is running.");
4758         }
4759         GrouperUtil.sleep(15000);
4760 
4761         int hasCreatedOn = HibernateSession.bySqlStatic().select(int.class, 
4762             "SELECT count(1) FROM grouper_change_log_entry_temp where created_on = ?", 
4763             GrouperUtil.toListObject(maxCreatedOn), HibUtils.listType(LongType.INSTANCE));
4764         
4765         //nothing to do
4766         if (hasCreatedOn == 0) {
4767           break;
4768         }
4769         
4770         int currentChangeLogEntryCount = HibernateSession.bySqlStatic().select(int.class, 
4771             "SELECT count(1) FROM grouper_change_log_entry_temp where created_on <= ?", 
4772             GrouperUtil.toListObject(maxCreatedOn), HibUtils.listType(LongType.INSTANCE));
4773           
4774         int loopsLimit = testingRunChangeLogSooner ? 2 : 100;
4775         
4776         if (loops > loopsLimit && changeLogEntryCount == currentChangeLogEntryCount) {
4777           //  daemon is not running, run the temp to change log
4778           int recordsChanged = ChangeLogTempToEntity.convertRecords();
4779           while (recordsChanged > 0) {
4780             hasCreatedOn = HibernateSession.bySqlStatic().select(int.class, 
4781                 "SELECT count(1) FROM grouper_change_log_entry_temp where created_on = ?", 
4782                 GrouperUtil.toListObject(maxCreatedOn), HibUtils.listType(LongType.INSTANCE));
4783             if (hasCreatedOn == 0) {
4784               break OUTER;
4785             }
4786             recordsChanged = ChangeLogTempToEntity.convertRecords();
4787           }
4788           break;
4789         }
4790         loops++;
4791       }
4792     }      
4793     PITUtils.deleteInactiveStem(stemName, printOutput);
4794 
4795   }
4796   
4797   /**
4798    * run the change log sooner for test
4799    */
4800   public static boolean testingRunChangeLogSooner = false;
4801   
4802   /**
4803    * Delete this stem from the Groups Registry including all sub objects.
4804    * @param printOutput
4805    * @param testOnly
4806    * @param deleteFromPointInTime needs to wait for change log entries to be processed, then delete those too
4807    */
4808   public void obliterate(final boolean printOutput, final boolean testOnly, final boolean deleteFromPointInTime) {
4809 
4810     this.obliterate(printOutput, testOnly);
4811     
4812     if (!testOnly && deleteFromPointInTime) {
4813       obliterateFromPointInTimeHelper(this.getName(), this, printOutput);
4814     }
4815 
4816   }
4817 
4818   /**
4819    * Delete this stem from the Groups Registry including all sub objects.
4820    * @param printOutput 
4821    * @param testOnly 
4822    * @throws  InsufficientPrivilegeException
4823    * @throws  StemDeleteException
4824    */
4825   public void obliterate(final boolean printOutput, final boolean testOnly) throws InsufficientPrivilegeException, StemDeleteException {
4826     
4827     if (printOutput) {
4828       if (testOnly) {
4829         System.out.println("Would obliterate stem: " + this.getName());
4830       } else {
4831         System.out.println("Obliterating stem: " + this.getName());
4832       }
4833     }
4834 
4835     StemObliterateResults stemObliterateResults = new StemObliterateResults();
4836     stemObliterateResultsThreadLocal.set(stemObliterateResults);
4837 
4838     if (GrouperConfig.retrieveConfig().propertyValueBoolean("grouper.obliterate.stem.in.transaction", false)) {
4839       
4840       HibernateSession.callbackHibernateSession(
4841           GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_NOT_AUDIT,
4842           new HibernateHandler() {
4843     
4844             public Object callback(HibernateHandlerBean hibernateHandlerBean)
4845                 throws GrouperDAOException {
4846     
4847               obliterateHelper(printOutput, testOnly, hibernateHandlerBean);
4848               
4849               return null;
4850            }
4851       });
4852     } else {
4853       obliterateHelper(printOutput, testOnly, null);
4854     }
4855   
4856     if (printOutput) {
4857       if (testOnly) {
4858         System.out.println("Would be done obliterating stem: " + this.getName());
4859       } else {
4860         System.out.println("Done obliterating stem: " + this.getName());
4861       }
4862     }    
4863   }
4864 
4865   /**
4866    * @see edu.internet2.middleware.grouper.grouperSet.GrouperSetElement#__getId()
4867    */
4868   public String __getId() {
4869     return this.getUuid();
4870   }
4871 
4872   /**
4873    * @see edu.internet2.middleware.grouper.grouperSet.GrouperSetElement#__getName()
4874    */
4875   public String __getName() {
4876     return this.getName();
4877   }
4878 
4879 
4880   /**
4881    * id of the group as a unique integer
4882    * @return id
4883    */
4884   public Long getIdIndex() {
4885     return this.idIndex;
4886   }
4887 
4888 
4889   /**
4890    * id of the group as a unique integer
4891    * @param idIndex1
4892    */
4893   public void setIdIndex(Long idIndex1) {
4894     this.idIndex = idIndex1;
4895   }
4896   
4897   /**
4898    * @see GrouperObject#matchesLowerSearchStrings(Set)
4899    */
4900   @Override
4901   public boolean matchesLowerSearchStrings(Set<String> filterStrings) {
4902 
4903     if (GrouperUtil.length(filterStrings) == 0) {
4904       return true;
4905     }
4906 
4907     String lowerId = this.getId().toLowerCase();
4908     String lowerName = StringUtils.defaultString(this.getName()).toLowerCase();
4909     String lowerDisplayName = StringUtils.defaultString(this.getDisplayName()).toLowerCase();
4910     String lowerDescription = StringUtils.defaultString(this.getDescription()).toLowerCase();
4911     String lowerAlternateName = StringUtils.defaultString(this.getAlternateName()).toLowerCase();
4912     
4913     for (String filterString : GrouperUtil.nonNull(filterStrings)) {
4914       
4915       //if all dont match, return false
4916       if (!lowerId.contains(filterString)
4917           && !lowerName.contains(filterString)
4918           && !lowerDisplayName.contains(filterString)
4919           && !lowerDescription.contains(filterString)
4920           && !lowerAlternateName.contains(filterString)) {
4921         return false;
4922       }
4923       
4924     }
4925     return true;
4926   }
4927 
4928   /**
4929    * see if the subject has a privilege or another privilege that implies this privilege.
4930    * @param subject
4931    * @param privilegeOrListName
4932    * @param secure if the user must be an admin to check
4933    * @return true if has privilege
4934    */
4935   public boolean canHavePrivilege(Subject subject, String privilegeOrListName, boolean secure) {
4936     
4937     GrouperSession grouperSession = GrouperSession.staticGrouperSession();
4938     
4939     if (secure) {
4940       PrivilegeHelper.dispatch(grouperSession, this, grouperSession.getSubject(), NamingPrivilege.STEM_ADMIN);
4941     }
4942     
4943     if (StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM.getName()) 
4944         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM.getListName())
4945         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ADMIN.getName()) 
4946         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ADMIN.getListName())) {
4947       return PrivilegeHelper.canStemAdmin(grouperSession, this, subject);
4948     }
4949     if (StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.CREATE.getName()) 
4950         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.CREATE.getListName())) {
4951       return PrivilegeHelper.canCreate(grouperSession, this, subject);
4952     }
4953     if (StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ATTR_READ.getName()) 
4954         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ATTR_READ.getListName())) {
4955       return PrivilegeHelper.canStemAttrRead(grouperSession, this, subject);
4956     }
4957     if (StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ATTR_UPDATE.getName()) 
4958         || StringUtils.equalsIgnoreCase(privilegeOrListName, NamingPrivilege.STEM_ATTR_UPDATE.getListName())) {
4959       return PrivilegeHelper.canStemAttrUpdate(grouperSession, this, subject);
4960     }
4961     throw new RuntimeException("Cant find privilege: '" + privilegeOrListName + "'");
4962   
4963   }
4964 
4965   /**
4966    * grant privs to stem
4967    * @param subject to add
4968    * @param createChecked
4969    * @param stemAdminChecked
4970    * @param attrReadChecked
4971    * @param attrUpdateChecked
4972    * @param revokeIfUnchecked
4973    * @return if something was changed
4974    */
4975   public boolean grantPrivs(final Subject subject,
4976       final boolean stemAdminChecked,
4977       final boolean createChecked, final boolean attrReadChecked,
4978       final boolean attrUpdateChecked, final boolean revokeIfUnchecked) {
4979     
4980     return (Boolean)GrouperTransaction.callbackGrouperTransaction(GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, new GrouperTransactionHandler() {
4981       
4982       @Override
4983       public Object callback(GrouperTransaction grouperTransaction)
4984           throws GrouperDAOException {
4985   
4986         boolean hadChange = false;
4987         
4988         //see if add or remove
4989         if (stemAdminChecked) {
4990           hadChange = hadChange | Stem.this.grantPriv(subject, NamingPrivilege.STEM_ADMIN, false);
4991         } else {
4992           if (revokeIfUnchecked) {
4993             hadChange = hadChange | Stem.this.revokePriv(subject, NamingPrivilege.STEM_ADMIN, false);
4994           }
4995         }
4996 
4997         //see if add or remove
4998         if (createChecked) {
4999           hadChange = hadChange | Stem.this.grantPriv(subject, NamingPrivilege.CREATE, false);
5000         } else {
5001           if (revokeIfUnchecked) {
5002             hadChange = hadChange | Stem.this.revokePriv(subject, NamingPrivilege.CREATE, false);
5003           }
5004         }
5005 
5006         //see if add or remove
5007         if (attrReadChecked) {
5008           hadChange = hadChange | Stem.this.grantPriv(subject, NamingPrivilege.STEM_ATTR_READ, false);
5009         } else {
5010           if (revokeIfUnchecked) {
5011             hadChange = hadChange | Stem.this.revokePriv(subject, NamingPrivilege.STEM_ATTR_READ, false);
5012           }
5013         }
5014 
5015         //see if add or remove
5016         if (attrUpdateChecked) {
5017           hadChange = hadChange | Stem.this.grantPriv(subject, NamingPrivilege.STEM_ATTR_UPDATE, false);
5018         } else {
5019           if (revokeIfUnchecked) {
5020             hadChange = hadChange | Stem.this.revokePriv(subject, NamingPrivilege.STEM_ATTR_UPDATE, false);
5021           }
5022         }
5023 
5024         return hadChange;
5025       }
5026     });
5027   
5028     
5029   }
5030 
5031   /**
5032    * counts when obliterating or seeing if can obliterate
5033    */
5034   public static class StemObliterateResults {
5035 
5036     /**
5037      * stem count total (even ones not allowed to delete)
5038      */
5039     private int stemCountTotal;
5040     
5041     /**
5042      * @return the stemCountTotal
5043      */
5044     public int getStemCountTotal() {
5045       return this.stemCountTotal;
5046     }
5047     
5048     /**
5049      * @param stemCountTotal1 the stemCountTotal to set
5050      */
5051     public void setStemCountTotal(int stemCountTotal1) {
5052       this.stemCountTotal = stemCountTotal1;
5053     }
5054 
5055     /**
5056      * group count total (even ones not allowed to delete)
5057      */
5058     private int groupCountTotal;
5059     
5060     /**
5061      * group count total (even ones not allowed to delete)
5062      * @return the groupCountTotal
5063      */
5064     public int getGroupCountTotal() {
5065       return this.groupCountTotal;
5066     }
5067 
5068     /**
5069      * group count total (even ones not allowed to delete)
5070      * @param groupCountTotal1 the groupCountTotal to set
5071      */
5072     public void setGroupCountTotal(int groupCountTotal1) {
5073       this.groupCountTotal = groupCountTotal1;
5074     }
5075 
5076     /**
5077      * attribute def count total (even ones arent allowed to delete)
5078      */
5079     private int attributeDefCountTotal;
5080     
5081     /**
5082      * attribute def count total (even ones arent allowed to delete)
5083      * @return the attributeDefCountTotal
5084      */
5085     public int getAttributeDefCountTotal() {
5086       return this.attributeDefCountTotal;
5087     }
5088     
5089     /**
5090      * attribute def count total (even ones arent allowed to delete)
5091      * @param attributeDefCountTotal the attributeDefCountTotal to set
5092      */
5093     public void setAttributeDefCountTotal(int attributeDefCountTotal) {
5094       this.attributeDefCountTotal = attributeDefCountTotal;
5095     }
5096 
5097     /**
5098      * attribute def name count total objects (even ones you arent allowed to delete)
5099      */
5100     private int attributeDefNameCountTotal;
5101     
5102     
5103     /**
5104      * attribute def name count total objects (even ones you arent allowed to delete)
5105      * @return the attributeDefNameCountTotal
5106      */
5107     public int getAttributeDefNameCountTotal() {
5108       return this.attributeDefNameCountTotal;
5109     }
5110     
5111     /**
5112      * attribute def name count total objects (even ones you arent allowed to delete)
5113      * @param attributeDefNameCountTotal1 the attributeDefNameCountTotal to set
5114      */
5115     public void setAttributeDefNameCountTotal(int attributeDefNameCountTotal1) {
5116       this.attributeDefNameCountTotal = attributeDefNameCountTotal1;
5117     }
5118 
5119     /**
5120      * stem count
5121      */
5122     private int stemCount;
5123     
5124     /**
5125      * stem count
5126      * @return the stemCount
5127      */
5128     public int getStemCount() {
5129       return this.stemCount;
5130     }
5131     
5132     /**
5133      * stem count
5134      * @param stemCount1 the stemCount to set
5135      */
5136     public void setStemCount(int stemCount1) {
5137       this.stemCount = stemCount1;
5138     }
5139     
5140     /**
5141      * group count
5142      */
5143     private int groupCount;
5144     
5145     /**
5146      * group count
5147      * @return the groupCount
5148      */
5149     public int getGroupCount() {
5150       return this.groupCount;
5151     }
5152     
5153     /**
5154      * group count
5155      * @param groupCount1 the groupCount to set
5156      */
5157     public void setGroupCount(int groupCount1) {
5158       this.groupCount = groupCount1;
5159     }
5160     
5161     /** attribute def count */
5162     private int attributeDefCount;
5163 
5164 
5165     
5166     /**
5167      * attribute def count
5168      * @return the attributeDefCount
5169      */
5170     public int getAttributeDefCount() {
5171       return this.attributeDefCount;
5172     }
5173 
5174 
5175     
5176     /**
5177      * attribute def count
5178      * @param attributeDefCount the attributeDefCount to set
5179      */
5180     public void setAttributeDefCount(int attributeDefCount) {
5181       this.attributeDefCount = attributeDefCount;
5182     }
5183     
5184     /**
5185      * attribute def name count
5186      */
5187     private int attributeDefNameCount;
5188 
5189 
5190     
5191     /**
5192      * attribute def name count
5193      * @return the attributeDefNameCount
5194      */
5195     public int getAttributeDefNameCount() {
5196       return this.attributeDefNameCount;
5197     }
5198 
5199 
5200     
5201     /**
5202      * attribute def name count
5203      * @param attributeDefNameCount1 the attributeDefNameCount to set
5204      */
5205     public void setAttributeDefNameCount(int attributeDefNameCount1) {
5206       this.attributeDefNameCount = attributeDefNameCount1;
5207     }
5208 
5209   }
5210 
5211   /**
5212    * keep results for obliterate
5213    */
5214   private static ThreadLocal<StemObliterateResults> stemObliterateResultsThreadLocal = new InheritableThreadLocal<StemObliterateResults>();
5215   
5216   /**
5217    * 
5218    * @return obliterate results
5219    */
5220   public static StemObliterateResults retrieveObliterateResults() {
5221     return stemObliterateResultsThreadLocal.get();
5222   }
5223   
5224   /**
5225    * @param printOutput
5226    * @param testOnly
5227    * @param hibernateHandlerBean
5228    */
5229   private void obliterateHelper(final boolean printOutput, final boolean testOnly,
5230       HibernateHandlerBean hibernateHandlerBean) {
5231     
5232     if (hibernateHandlerBean != null) {
5233       hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
5234     }
5235     
5236     StemObliterateResults stemObliterateResults = retrieveObliterateResults();
5237 
5238     {
5239       //obliterate all child stems
5240       Set<Stem> stems = GrouperDAOFactory.getFactory().getStem().findAllChildStems(Stem.this, Scope.ONE);
5241   
5242       // done below
5243       //stemObliterateResults.setStemCount(stemObliterateResults.getStemCount() + GrouperUtil.length(stems));
5244       
5245       for (Stem stem : GrouperUtil.nonNull(stems)) {
5246         stem.obliterateHelper(printOutput, testOnly, hibernateHandlerBean);
5247       }
5248     }
5249     
5250     {
5251       Set<Group> groups = deleteGroups(printOutput, testOnly, Scope.ONE);
5252   
5253       stemObliterateResults.setGroupCount(stemObliterateResults.getGroupCount() + GrouperUtil.length(groups));
5254     }
5255     
5256     {
5257       Set<AttributeDefName> attributeDefNames = deleteAttributeDefNames(printOutput, testOnly, Scope.ONE);
5258   
5259       stemObliterateResults.setAttributeDefNameCount(stemObliterateResults.getAttributeDefNameCount() + GrouperUtil.length(attributeDefNames));
5260     }
5261     
5262     {
5263       Set<AttributeDef> attributeDefs = deleteAttributeDefs(printOutput, testOnly, Scope.ONE);
5264   
5265       stemObliterateResults.setAttributeDefCount(stemObliterateResults.getAttributeDefCount() + GrouperUtil.length(attributeDefs));
5266     }
5267     
5268     //delete stem
5269     if (!testOnly) {
5270       Stem.this.delete();
5271     }
5272     stemObliterateResults.setStemCount(stemObliterateResults.getStemCount() + 1);
5273   }
5274 
5275   /**
5276    * 
5277    * @param args
5278    */
5279   public static void main(String[] args) {
5280     GrouperSession.startRootSession();
5281     GrouperSession grouperSession = GrouperSession.start(SubjectFinder.findById("test.subject.0", true));
5282     
5283     Stem stem = StemFinder.findByName(grouperSession, "test", true);
5284     QueryOptionsl/dao/QueryOptions.html#QueryOptions">QueryOptions queryOptions = new QueryOptions().retrieveCount(true).retrieveResults(false);
5285     Set<AttributeDefName> attributeDefNames = new AttributeDefNameFinder().assignParentStemId(stem.getUuid()).assignStemScope(Scope.SUB)
5286     .assignPrivileges(AttributeDefPrivilege.ATTR_ADMIN_PRIVILEGES)
5287     .assignQueryOptions(queryOptions).findAttributeNames();
5288     System.out.println(queryOptions.getCount());
5289     System.out.println(GrouperUtil.length(attributeDefNames));
5290   }
5291   
5292   /**
5293    * 
5294    * @return if this stem is empty i.e. can be deleted
5295    */
5296   public boolean isEmpty() {
5297     return (Boolean)GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
5298       
5299       public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
5300         Set<Stem> stems = GrouperDAOFactory.getFactory().getStem().findAllChildStems(Stem.this, Scope.ONE);
5301         if (GrouperUtil.length(stems) > 0) {
5302           return false;
5303         }
5304         Set<Group> groups = GrouperDAOFactory.getFactory().getStem().findAllChildGroups(Stem.this, Scope.ONE);
5305         if (GrouperUtil.length(groups) > 0) {
5306           return false;
5307         }
5308         Set<AttributeDefName> attributeDefNames = new AttributeDefNameFinder().assignParentStemId(Stem.this.uuid).assignStemScope(Scope.ONE)
5309               .findAttributeNames();
5310         if (GrouperUtil.length(attributeDefNames) > 0) {
5311           return false;
5312         }
5313         Set<AttributeDef> attributeDefs = new AttributeDefFinder().assignParentStemId(Stem.this.uuid).assignStemScope(Scope.ONE)
5314               .findAttributes();
5315         if (GrouperUtil.length(attributeDefs) > 0) {
5316           return false;
5317         }
5318         return true;
5319       }
5320     });
5321   }
5322   
5323   /**
5324    * see if the current session can obliterate.  also setup counts of object types
5325    * @return true if can obliterate
5326    */
5327   public boolean isCanObliterate() {
5328 
5329     final StemObliterateResults stemObliterateResults = new StemObliterateResults();
5330     stemObliterateResultsThreadLocal.set(stemObliterateResults);
5331 
5332     Subject subject = GrouperSession.staticGrouperSession().getSubject();
5333     QueryOptionsl/dao/QueryOptions.html#QueryOptions">QueryOptions queryOptions = new QueryOptions().retrieveCount(true).retrieveResults(false);
5334     new AttributeDefFinder().assignParentStemId(this.uuid).assignStemScope(Scope.SUB)
5335         .assignSubject(subject)
5336         .assignPrivileges(AttributeDefPrivilege.ATTR_ADMIN_PRIVILEGES)
5337         .assignQueryOptions(queryOptions).findAttributes();
5338     stemObliterateResults.setAttributeDefCount(GrouperUtil.intValue(queryOptions.getCount(), -1));
5339     
5340     queryOptions = new QueryOptions().retrieveCount(true).retrieveResults(false);
5341     new AttributeDefNameFinder().assignParentStemId(this.uuid).assignStemScope(Scope.SUB)
5342         .assignPrivileges(AttributeDefPrivilege.ATTR_ADMIN_PRIVILEGES)
5343         .assignSubject(subject)
5344         .assignQueryOptions(queryOptions).findAttributeNames();
5345     stemObliterateResults.setAttributeDefNameCount(GrouperUtil.intValue(queryOptions.getCount(), -1));
5346     
5347     queryOptions = new QueryOptions().retrieveCount(true).retrieveResults(false);
5348     new GroupFinder().assignStemScope(Scope.SUB).assignParentStemId(this.uuid)
5349         .assignPrivileges(AccessPrivilege.ADMIN_PRIVILEGES)
5350         .assignQueryOptions(queryOptions).findGroups();
5351     stemObliterateResults.setGroupCount(GrouperUtil.intValue(queryOptions.getCount(), -1));
5352 
5353     queryOptions = new QueryOptions().retrieveCount(true).retrieveResults(false);
5354     new StemFinder().assignStemScope(Scope.SUB).assignParentStemId(this.uuid)
5355         .assignPrivileges(NamingPrivilege.ADMIN_PRIVILEGES)
5356         .assignQueryOptions(queryOptions).findStems();
5357     stemObliterateResults.setStemCount(GrouperUtil.intValue(queryOptions.getCount(), -1));
5358     if (stemObliterateResults.getStemCount() >= 0 && this.canHavePrivilege(subject, NamingPrivilege.STEM_ADMIN.toString(), false)) {
5359       // add one for parent folder
5360       stemObliterateResults.setStemCount(stemObliterateResults.getStemCount()+1);
5361     }
5362     
5363     boolean isWheelOrRoot = PrivilegeHelper.isWheelOrRoot(subject);
5364     //TODO: if an inheritable admin then yes
5365 
5366     if (isWheelOrRoot) {
5367       stemObliterateResults.setAttributeDefCountTotal(stemObliterateResults.getAttributeDefCount());
5368       stemObliterateResults.setAttributeDefNameCountTotal(stemObliterateResults.getAttributeDefNameCount());
5369       stemObliterateResults.setStemCountTotal(stemObliterateResults.getStemCount());
5370       stemObliterateResults.setGroupCountTotal(stemObliterateResults.getGroupCount());
5371       return true;
5372     }
5373 
5374     GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
5375       
5376       public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
5377 
5378         Subject subject = SubjectFinder.findRootSubject();
5379         QueryOptionsl/dao/QueryOptions.html#QueryOptions">QueryOptions queryOptions = new QueryOptions().retrieveCount(true).retrieveResults(false);
5380         new AttributeDefFinder().assignParentStemId(Stem.this.uuid).assignStemScope(Scope.SUB)
5381             .assignQueryOptions(queryOptions).findAttributes();
5382         stemObliterateResults.setAttributeDefCountTotal(GrouperUtil.intValue(queryOptions.getCount(), -1));
5383         
5384         queryOptions = new QueryOptions().retrieveCount(true).retrieveResults(false);
5385         new AttributeDefNameFinder().assignParentStemId(Stem.this.uuid).assignStemScope(Scope.SUB)
5386             .assignQueryOptions(queryOptions).findAttributeNames();
5387         stemObliterateResults.setAttributeDefNameCountTotal(GrouperUtil.intValue(queryOptions.getCount(), -1));
5388         
5389         queryOptions = new QueryOptions().retrieveCount(true).retrieveResults(false);
5390         new GroupFinder().assignStemScope(Scope.SUB).assignParentStemId(Stem.this.uuid)
5391             .assignQueryOptions(queryOptions).findGroups();
5392         stemObliterateResults.setGroupCountTotal(GrouperUtil.intValue(queryOptions.getCount(), -1));
5393 
5394         queryOptions = new QueryOptions().retrieveCount(true).retrieveResults(false);
5395         new StemFinder().assignStemScope(Scope.SUB).assignParentStemId(Stem.this.uuid)
5396             .assignQueryOptions(queryOptions).findStems();
5397         stemObliterateResults.setStemCountTotal(GrouperUtil.intValue(queryOptions.getCount(), -1));
5398 
5399         if (stemObliterateResults.getStemCount() >= 0) {
5400           // add one for parent folder
5401           stemObliterateResults.setStemCountTotal(stemObliterateResults.getStemCountTotal()+1);
5402         }
5403 
5404         return null;
5405       }
5406     });
5407 
5408     //see if counts match
5409     if (stemObliterateResults.getAttributeDefCount() != stemObliterateResults.getAttributeDefCountTotal()) {
5410       return false;
5411     }
5412     if (stemObliterateResults.getAttributeDefNameCount() != stemObliterateResults.getAttributeDefNameCountTotal()) {
5413       return false;
5414     }
5415     if (stemObliterateResults.getStemCount() != stemObliterateResults.getStemCountTotal()) {
5416       return false;
5417     }
5418     if (stemObliterateResults.getGroupCount() != stemObliterateResults.getGroupCountTotal()) {
5419       return false;
5420     }
5421     return true;
5422     
5423     
5424   }
5425   
5426   /**
5427    * @param printOutput
5428    * @param testOnly
5429    * @param scope
5430    * @return attribute defs to be deleted or would be deleted
5431    */
5432   public Set<AttributeDef> deleteAttributeDefs(final boolean printOutput, final boolean testOnly, Scope scope) {
5433 
5434     Set<AttributeDef> attributeDefs = new AttributeDefFinder().assignParentStemId(this.uuid).assignStemScope(scope)
5435         .assignSubject(GrouperSession.staticGrouperSession().getSubject())
5436         .assignPrivileges(AttributeDefPrivilege.ATTR_ADMIN_PRIVILEGES).findAttributes();
5437     
5438     Set<AttributeDef> deletedObjects = new HashSet<AttributeDef>();
5439 
5440     for (AttributeDef attributeDef : GrouperUtil.nonNull(attributeDefs)) {
5441       
5442       deletedObjects.add(attributeDef);
5443 
5444       if (!testOnly) {
5445         attributeDef.delete();
5446       }
5447       if (printOutput) {
5448         if (testOnly) {
5449           System.out.println("Would be done deleting attributeDef: " + attributeDef.getName());
5450         } else {
5451           System.out.println("Done deleting attributeDef: " + attributeDef.getName());
5452         }
5453       }              
5454     }
5455     return deletedObjects;
5456 
5457   }
5458 
5459   /**
5460    * @param printOutput
5461    * @param testOnly
5462    * @param scope
5463    * @return attribute def names deleted or to be deleted
5464    */
5465   public Set<AttributeDefName> deleteAttributeDefNames(final boolean printOutput, final boolean testOnly, Scope scope) {
5466 
5467     Set<AttributeDefName> attributeDefNames = new AttributeDefNameFinder().assignParentStemId(this.uuid).assignStemScope(scope)
5468       .assignPrivileges(AttributeDefPrivilege.ATTR_ADMIN_PRIVILEGES).assignSubject(GrouperSession.staticGrouperSession().getSubject()).findAttributeNames();
5469     
5470     Set<AttributeDefName> deletedObjects = new HashSet<AttributeDefName>();
5471 
5472     for (AttributeDefName attributeDefName : GrouperUtil.nonNull(attributeDefNames)) {
5473       deletedObjects.add(attributeDefName);
5474       if (!testOnly) {
5475         attributeDefName.delete();
5476       }
5477       if (printOutput) {
5478         if (testOnly) {
5479           System.out.println("Would be done deleting attributeDefName: " + attributeDefName.getName());
5480         } else {
5481           System.out.println("Done deleting attributeDefName: " + attributeDefName.getName());
5482         }
5483       }              
5484     }
5485     return deletedObjects;
5486   }
5487 
5488   /**
5489    * @param printOutput
5490    * @param testOnly
5491    * @param scope 
5492    * @return groups to be deleted or would be deleted
5493    */
5494   public Set<Group> deleteGroups(final boolean printOutput, final boolean testOnly, Scope scope) {
5495     //delete all objects
5496     //groups
5497     Set<Group> groups = new GroupFinder().assignStemScope(scope).assignParentStemId(this.uuid)
5498         .assignPrivileges(AccessPrivilege.ADMIN_PRIVILEGES).findGroups();
5499     
5500     Set<Group> deletedObjects = new HashSet<Group>();
5501     
5502     //pass one for composites since if you delete a factor first it bombs...
5503     for (int i : new int[]{0,1}) {
5504       Iterator<Group> groupIterator = groups.iterator();
5505       while (groupIterator.hasNext()) {
5506         Group group = groupIterator.next();
5507         
5508         if (i==0 && !group.isHasComposite()) {
5509           continue;
5510         }
5511         
5512         deletedObjects.add(group);
5513         
5514         if (!testOnly) {
5515           group.delete();
5516         }
5517         if (printOutput) {
5518           if (testOnly) {
5519             System.out.println("Would be done deleting " + group.getTypeOfGroup() + ": " + group.getName());
5520           } else {
5521             System.out.println("Done deleting " + group.getTypeOfGroup() + ": " + group.getName());
5522           }
5523         }
5524         groupIterator.remove();
5525       }
5526     }
5527     
5528     return deletedObjects;
5529     
5530   }
5531 
5532   /**
5533    * @param printOutput
5534    * @param testOnly
5535    * @param scope 
5536    * @return groups have memberships deleted or would be deleted
5537    */
5538   public Set<Group> deleteGroupMemberships(boolean printOutput, final boolean testOnly, Scope scope) {
5539     //delete all objects
5540     //groups
5541     Set<Group> groups = new GroupFinder().assignStemScope(scope).assignParentStemId(this.uuid)
5542         .assignPrivileges(AccessPrivilege.UPDATE_PRIVILEGES).addTypeOfGroup(TypeOfGroup.group).addTypeOfGroup(TypeOfGroup.role).findGroups();
5543     
5544     Set<Group> deletedObjects = new HashSet<Group>();
5545     
5546     //pass one for composites since if you delete a factor first it bombs...
5547     Iterator<Group> groupIterator = groups.iterator();
5548     while (groupIterator.hasNext()) {
5549       Group group = groupIterator.next();
5550       
5551       deletedObjects.add(group);
5552       
5553       if (!testOnly) {
5554         group.deleteAllMemberships();
5555       }
5556       if (printOutput) {
5557         if (testOnly) {
5558           System.out.println("Would be done deleting memberships from " + group.getTypeOfGroup() + ": " + group.getName());
5559         } else {
5560           System.out.println("Done deleting memberships from " + group.getTypeOfGroup() + ": " + group.getName());
5561         }
5562       }
5563       groupIterator.remove();
5564     }
5565     
5566     return deletedObjects;
5567     
5568   }
5569 
5570   /**
5571    * @param printOutput
5572    * @param testOnly
5573    * @param scope 
5574    * @return stems to be deleted or would be deleted
5575    */
5576   public Set<Stem> deleteEmptyStems(final boolean printOutput, final boolean testOnly, Scope scope) {
5577     //delete all non empty stems
5578     //stems sorted by name desc so if stems contain empty stems they will be deleted
5579     Set<Stem> stems = new StemFinder().assignStemScope(scope).assignParentStemId(this.uuid)
5580         .assignPrivileges(NamingPrivilege.ADMIN_PRIVILEGES).assignQueryOptions(new QueryOptions().sortDesc("name")).findStems();
5581 
5582     Set<Stem> deletedObjects = new HashSet<Stem>();
5583 
5584     Iterator<Stem> stemIterator = stems.iterator();
5585     while (stemIterator.hasNext()) {
5586       Stem stem = stemIterator.next();
5587       if (!stem.isEmpty()) {
5588         continue;
5589       }
5590       deletedObjects.add(stem);
5591       if (!testOnly) {
5592         stem.delete();
5593       }
5594       if (printOutput) {
5595         if (testOnly) {
5596           System.out.println("Would be done deleting stem: " + stem.getName());
5597         } else {
5598           System.out.println("Done deleting stem: " + stem.getName());
5599         }
5600       }
5601     }
5602     return deletedObjects;
5603   }
5604 
5605 }
5606