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   * 
18   */
19  package edu.internet2.middleware.grouper.attr.value;
20  
21  import java.io.StringWriter;
22  import java.sql.Timestamp;
23  import java.text.ParseException;
24  import java.text.SimpleDateFormat;
25  import java.util.Collection;
26  import java.util.Date;
27  import java.util.List;
28  import java.util.Set;
29  
30  import org.apache.commons.lang.ObjectUtils;
31  import org.apache.commons.lang.StringUtils;
32  import org.apache.commons.logging.Log;
33  import org.hibernate.type.DoubleType;
34  import org.hibernate.type.LongType;
35  import org.hibernate.type.StringType;
36  import org.hibernate.type.Type;
37  
38  import edu.internet2.middleware.grouper.Attribute;
39  import edu.internet2.middleware.grouper.GrouperAPI;
40  import edu.internet2.middleware.grouper.Member;
41  import edu.internet2.middleware.grouper.attr.AttributeDef;
42  import edu.internet2.middleware.grouper.attr.AttributeDefName;
43  import edu.internet2.middleware.grouper.attr.AttributeDefType;
44  import edu.internet2.middleware.grouper.attr.AttributeDefValueType;
45  import edu.internet2.middleware.grouper.attr.assign.AttributeAssign;
46  import edu.internet2.middleware.grouper.audit.AuditEntry;
47  import edu.internet2.middleware.grouper.audit.AuditTypeBuiltin;
48  import edu.internet2.middleware.grouper.cfg.GrouperConfig;
49  import edu.internet2.middleware.grouper.changeLog.ChangeLogEntry;
50  import edu.internet2.middleware.grouper.changeLog.ChangeLogLabels;
51  import edu.internet2.middleware.grouper.changeLog.ChangeLogTypeBuiltin;
52  import edu.internet2.middleware.grouper.entity.Entity;
53  import edu.internet2.middleware.grouper.entity.EntityUtils;
54  import edu.internet2.middleware.grouper.exception.LimitInvalidException;
55  import edu.internet2.middleware.grouper.hibernate.AuditControl;
56  import edu.internet2.middleware.grouper.hibernate.BySql;
57  import edu.internet2.middleware.grouper.hibernate.GrouperTransactionType;
58  import edu.internet2.middleware.grouper.hibernate.HibUtils;
59  import edu.internet2.middleware.grouper.hibernate.HibernateHandler;
60  import edu.internet2.middleware.grouper.hibernate.HibernateHandlerBean;
61  import edu.internet2.middleware.grouper.hibernate.HibernateSession;
62  import edu.internet2.middleware.grouper.hooks.AttributeAssignValueHooks;
63  import edu.internet2.middleware.grouper.hooks.AttributeHooks;
64  import edu.internet2.middleware.grouper.hooks.beans.HooksAttributeAssignValueBean;
65  import edu.internet2.middleware.grouper.hooks.beans.HooksAttributeBean;
66  import edu.internet2.middleware.grouper.hooks.logic.GrouperHookType;
67  import edu.internet2.middleware.grouper.hooks.logic.GrouperHooksUtils;
68  import edu.internet2.middleware.grouper.hooks.logic.VetoTypeGrouper;
69  import edu.internet2.middleware.grouper.internal.dao.GrouperDAOException;
70  import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GrouperVersioned;
71  import edu.internet2.middleware.grouper.internal.util.GrouperUuid;
72  import edu.internet2.middleware.grouper.misc.GrouperDAOFactory;
73  import edu.internet2.middleware.grouper.misc.GrouperHasContext;
74  import edu.internet2.middleware.grouper.misc.GrouperVersion;
75  import edu.internet2.middleware.grouper.permissions.limits.PermissionLimitDocumentation;
76  import edu.internet2.middleware.grouper.permissions.limits.PermissionLimitInterface;
77  import edu.internet2.middleware.grouper.permissions.limits.PermissionLimitUtils;
78  import edu.internet2.middleware.grouper.util.GrouperUtil;
79  import edu.internet2.middleware.grouper.xml.export.XmlExportAttributeAssignValue;
80  import edu.internet2.middleware.grouper.xml.export.XmlImportableMultiple;
81  
82  
83  /**
84   * value of an attribute assignment (could be multi-valued based on the attributeDef
85   * @author mchyzer
86   *
87   */
88  @SuppressWarnings("serial")
89  public class AttributeAssignValue extends GrouperAPI implements GrouperHasContext, Hib3GrouperVersioned, XmlImportableMultiple<AttributeAssignValue> {
90  
91    /** logger */
92    @SuppressWarnings("unused")
93    private static final Log LOG = GrouperUtil.getLog(AttributeAssignValue.class);
94  
95    /** name of the groups attribute def table in the db */
96    public static final String TABLE_GROUPER_ATTRIBUTE_ASSIGN_VALUE = "grouper_attribute_assign_value";
97  
98    /** column */
99    public static final String COLUMN_CONTEXT_ID = "context_id";
100 
101   /** column */
102   public static final String COLUMN_CREATED_ON = "created_on";
103 
104   /** column */
105   public static final String COLUMN_LAST_UPDATED = "last_updated";
106 
107   /** column */
108   public static final String COLUMN_ID = "id";
109 
110   /** column */
111   public static final String COLUMN_VALUE_STRING = "value_string";
112 
113   /** column */
114   public static final String COLUMN_VALUE_FLOATING = "value_floating";
115 
116   /** column */
117   public static final String COLUMN_VALUE_INTEGER = "value_integer";
118 
119   /** column */
120   public static final String COLUMN_VALUE_MEMBER_ID = "value_member_id";
121 
122   /** column */
123   public static final String COLUMN_ATTRIBUTE_ASSIGN_ID = "attribute_assign_id";
124 
125   
126   //*****  START GENERATED WITH GenerateFieldConstants.java *****//
127 
128   /** constant for field name for: attributeAssignId */
129   public static final String FIELD_ATTRIBUTE_ASSIGN_ID = "attributeAssignId";
130 
131   /** constant for field name for: contextId */
132   public static final String FIELD_CONTEXT_ID = "contextId";
133 
134   /** constant for field name for: createdOnDb */
135   public static final String FIELD_CREATED_ON_DB = "createdOnDb";
136 
137   /** constant for field name for: id */
138   public static final String FIELD_ID = "id";
139 
140   /** constant for field name for: lastUpdatedDb */
141   public static final String FIELD_LAST_UPDATED_DB = "lastUpdatedDb";
142 
143   /** constant for field name for: valueFloating */
144   public static final String FIELD_VALUE_FLOATING = "valueFloating";
145 
146   /** constant for field name for: valueInteger */
147   public static final String FIELD_VALUE_INTEGER = "valueInteger";
148 
149   /** constant for field name for: valueMemberId */
150   public static final String FIELD_VALUE_MEMBER_ID = "valueMemberId";
151 
152   /** constant for field name for: valueString */
153   public static final String FIELD_VALUE_STRING = "valueString";
154 
155   /**
156    * fields which are included in db version
157    */
158   private static final Set<String> DB_VERSION_FIELDS = GrouperUtil.toSet(
159       FIELD_ATTRIBUTE_ASSIGN_ID, FIELD_CONTEXT_ID, FIELD_CREATED_ON_DB, FIELD_ID, 
160       FIELD_LAST_UPDATED_DB, FIELD_VALUE_INTEGER, FIELD_VALUE_MEMBER_ID, FIELD_VALUE_STRING,
161       FIELD_VALUE_FLOATING);
162 
163   /**
164    * fields which are included in clone method
165    */
166   private static final Set<String> CLONE_FIELDS = GrouperUtil.toSet(
167       FIELD_ATTRIBUTE_ASSIGN_ID, FIELD_CONTEXT_ID, FIELD_CREATED_ON_DB, FIELD_HIBERNATE_VERSION_NUMBER, 
168       FIELD_ID, FIELD_LAST_UPDATED_DB, FIELD_VALUE_INTEGER, FIELD_VALUE_MEMBER_ID, 
169       FIELD_VALUE_STRING);
170 
171   //*****  END GENERATED WITH GenerateFieldConstants.java *****//
172 
173   /**
174    * deep clone the fields in this object
175    */
176   @Override
177   public AttributeAssignValue clone() {
178     return GrouperUtil.clone(this, CLONE_FIELDS);
179   }
180 
181   /** the cached assignment */
182   private AttributeAssign attributeAssign;
183   
184   /** attribute assignment in this value assignment */
185   private String attributeAssignId;
186 
187   /** id of this attribute def */
188   private String id;
189 
190   /** string value */
191   private String valueString;
192 
193   /** floating point value */
194   private Double valueFloating;
195 
196   /** integer value */
197   private Long valueInteger;
198 
199   /**
200    * floating point value
201    * @return floating point value
202    */
203   public Double getValueFloating() {
204     return this.valueFloating;
205   }
206 
207   /**
208    * floating point value
209    * @param valueFloating1
210    */
211   public void setValueFloating(Double valueFloating1) {
212     this.valueFloating = valueFloating1;
213   }
214 
215   /**
216    * assign a value to any type
217    * @param value
218    */
219   public void assignValue(Object value) {
220     AttributeAssign attributeAssign = this.getAttributeAssign();
221     if (attributeAssign == null) {
222       throw new RuntimeException("You need to set the attributeAssignId or the attributeDef to set the value");
223     }
224     AttributeDef attributeDef = attributeAssign.getAttributeDef();
225     this.assignValue(value, attributeDef);
226   }
227   
228   /**
229    * assign a value to any type
230    * @param value
231    * @param attributeDef
232    */
233   public void assignValue(Object value, AttributeDef attributeDef) {
234     
235     AttributeDefValueType attributeDefValueType = attributeDef.getValueType();
236     
237     this.clearValue();
238     
239     if (GrouperUtil.isBlank(value)) {
240       return;
241     }
242     
243     switch(attributeDefValueType) {
244       case timestamp:
245         Timestamp timestamp = GrouperUtil.toTimestamp(value);
246         this.valueInteger = timestamp == null ? null : timestamp.getTime();
247         break;
248       case floating:
249         this.valueFloating = GrouperUtil.doubleValue(value);
250         break;
251       case integer:
252         this.valueInteger = GrouperUtil.longValue(value);
253         break;
254       case marker:
255         throw new RuntimeException("Cant assign a value to a marker attribute: " 
256             + value + ", " + this.attributeAssignId); 
257       case memberId:
258         this.valueMemberId = GrouperUtil.stringValue(value);
259         break;
260       case string:
261         this.valueString = GrouperUtil.stringValue(value);
262         break;
263       default:
264         throw new RuntimeException("Not expecting type: " + attributeDefValueType);
265     }
266   }
267 
268   /**
269    * whatever the type, return the string value
270    * @return value
271    */
272   public String valueString() {
273     return valueString(false);
274   }
275 
276   /**
277    * get value string value
278    * @return value string friendly
279    */
280   public String getValueFriendly() {
281     return this.valueString(true);
282   }
283   
284   /**
285    * whatever the type, return the string value
286    * @param convertTimestampToFriendly true to convert timestamps to yyyy/MM/dd HH:mm:ss.SSS 
287    * as opposed to numbers of millis since 1970
288    * @return value
289    */
290   public String valueString(boolean convertTimestampToFriendly) {
291     
292     AttributeAssign attributeAssign = this.getAttributeAssign();
293     AttributeDef attributeDef = attributeAssign.getAttributeDef();
294     
295     AttributeDefValueType attributeDefValueType = attributeDef.getValueType();
296     
297     switch(attributeDefValueType) {
298       case floating:
299         return this.valueFloating == null ? null : this.valueFloating.toString();
300       case integer:
301         return this.valueInteger == null ? null : this.valueInteger.toString();
302       case marker:
303         throw new RuntimeException("Why would a marker attribute have a value? " + this);
304       case memberId:
305         return this.valueMemberId;
306       case string:
307         return this.valueString;
308       case timestamp:
309         if (this.valueInteger == null) {
310           return null;
311         }
312         if (convertTimestampToFriendly) {
313           return dateToString(new Timestamp(this.valueInteger));
314         }
315         return this.valueInteger.toString();
316       default:
317         throw new RuntimeException("Not expecting type: " + attributeDefValueType);
318     }
319   }
320 
321   /**
322    * Note, this is 
323    * web service format string
324    */
325   private static final String WS_DATE_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS";
326 
327   /**
328    * Note, this is 
329    * web service format string
330    */
331   private static final String WS_DATE_FORMAT2 = "yyyy/MM/dd_HH:mm:ss.SSS";
332 
333   /**
334    * convert a date to a string using the standard web service pattern
335    * yyyy/MM/dd HH:mm:ss.SSS Note that HH is 0-23
336    * 
337    * @param date
338    * @return the string, or null if the date is null
339    */
340   public static String dateToString(Date date) {
341     if (date == null) {
342       return null;
343     }
344     SimpleDateFormat simpleDateFormat = new SimpleDateFormat(WS_DATE_FORMAT);
345     return simpleDateFormat.format(date);
346   }
347 
348   /**
349    * convert a string to a date using the standard web service pattern Note
350    * that HH is 0-23
351    * 
352    * @param dateString
353    * @return the string, or null if the date was null
354    */
355   public static Date stringToDate(String dateString) {
356     if (StringUtils.isBlank(dateString)) {
357       return null;
358     }
359     SimpleDateFormat simpleDateFormat = new SimpleDateFormat(WS_DATE_FORMAT);
360     try {
361       return simpleDateFormat.parse(dateString);
362     } catch (ParseException e) {
363       SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat(WS_DATE_FORMAT2);
364       try {
365         return simpleDateFormat2.parse(dateString);
366       } catch (ParseException e2) {
367         throw new RuntimeException("Cannot convert '" + dateString
368             + "' to a date based on format: " + WS_DATE_FORMAT, e);
369       }
370     }
371   }
372 
373   
374 
375   
376   /**
377    * clear all the values
378    */
379   public void clearValue() {
380     this.valueFloating = null;
381     this.valueInteger = null;
382     this.valueMemberId = null;
383     this.valueString = null;
384     
385   }
386   
387   /**
388    * clear all the values
389    * @param attributeAssignValue 
390    */
391   public void assignValue(AttributeAssignValue attributeAssignValue) {
392     this.valueFloating = attributeAssignValue.valueFloating;
393     this.valueInteger = attributeAssignValue.valueInteger;
394     this.valueMemberId = attributeAssignValue.valueMemberId;
395     this.valueString = attributeAssignValue.valueString;
396   }
397   
398   /** member id value */
399   private String valueMemberId;
400 
401   /** context id of the transaction */
402   private String contextId;
403 
404   /**
405    * time in millis when this attribute was last modified
406    */
407   private Long lastUpdatedDb;
408 
409   /**
410    * time in millis when this attribute was created
411    */
412   private Long createdOnDb;
413 
414   /**
415    * validate the value
416    */
417   private void validateValue() {
418     AttributeAssign attributeAssign = this.getAttributeAssign();
419     if (attributeAssign == null) {
420       throw new RuntimeException("You need to set the attributeAssignId or the attributeDef to set the value");
421     }
422     AttributeDef attributeDef = attributeAssign.getAttributeDef();
423     AttributeDefValueType attributeDefValueType = attributeDef.getValueType();
424     
425     switch(attributeDefValueType) {
426       case timestamp:
427         if (this.valueFloating != null || this.valueMemberId != null || this.valueString != null) {
428           throw new RuntimeException(attributeDefValueType + " value has wrong value type");
429         }
430         break;
431       case floating:
432         if (this.valueInteger != null || this.valueMemberId != null || this.valueString != null) {
433           throw new RuntimeException(attributeDefValueType + " value has wrong value type");
434         }
435         break;
436       case integer:
437         if (this.valueFloating != null || this.valueMemberId != null || this.valueString != null) {
438           throw new RuntimeException(attributeDefValueType + " value has wrong value type");
439         }
440         break;
441       case marker:
442         if (this.valueInteger != null || this.valueFloating != null || this.valueMemberId != null || this.valueString != null) {
443           throw new RuntimeException(attributeDefValueType + " value has wrong value type");
444         }
445         break;
446       case memberId:
447         if (this.valueInteger != null || this.valueFloating != null || this.valueString != null) {
448           throw new RuntimeException(attributeDefValueType + " value has wrong value type");
449         }
450         break;
451       case string:
452         if (this.valueInteger != null || this.valueFloating != null || this.valueMemberId != null) {
453           throw new RuntimeException(attributeDefValueType + " value has wrong value type");
454         }
455         break;
456       default:
457         throw new RuntimeException("Not expecting type: " + attributeDefValueType);
458     }
459   }
460   
461   /**
462    * keep a count for junit
463    */
464   public static long testingUseSqlCount = 0;
465   
466   /**
467    * save or update this object
468    */
469   public void saveOrUpdate() {
470     
471     if (StringUtils.isBlank(this.id)) {
472       this.id = GrouperUuid.getUuid();
473     }
474     
475     this.validateValue();
476     
477     final boolean isInsert = ObjectUtils.equals(this.getHibernateVersionNumber(), GrouperAPI.INITIAL_VERSION_NUMBER);
478 
479     HibernateSession.callbackHibernateSession(
480         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
481         new HibernateHandler() {
482 
483           public Object callback(HibernateHandlerBean hibernateHandlerBean)
484               throws GrouperDAOException {
485             
486             AttributeAssign attributeAssign = AttributeAssignValue.this.getAttributeAssign();
487             AttributeDefName attributeDefName = attributeAssign.getAttributeDefName();
488 
489             hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
490 
491             String differences = null;
492             if (!hibernateHandlerBean.isCallerWillCreateAudit() && !isInsert) {
493               differences = GrouperUtil.dbVersionDescribeDifferences(AttributeAssignValue.this.dbVersion(), 
494                   AttributeAssignValue.this, AttributeAssignValue.this.dbVersion() != null ? AttributeAssignValue.this.dbVersionDifferentFields() : AttributeAssignValue.CLONE_FIELDS);
495             }
496             
497             boolean isLegacyAttributeUpdate = false;
498             Attribute attribute = Attribute.internal_getAttribute(AttributeAssignValue.this, null, false);
499             
500             GrouperConfig grouperConfig = GrouperConfig.retrieveConfig();
501             
502             Set<String> attributeDefNameIdsToIgnoreChangeLogAndAudit = grouperConfig.attributeDefNameIdsToIgnoreChangeLogAndAudit();
503             boolean attributeDefNameIdIgnoreChangeLog = attributeDefNameIdsToIgnoreChangeLogAndAudit.contains(attributeDefName.getId());
504 
505             Set<String> attributeDefIdsToIgnoreChangeLogAndAudit = grouperConfig.attributeDefIdsToIgnoreChangeLogAndAudit();
506             boolean attributeDefIdIgnoreChangeLog = attributeDefIdsToIgnoreChangeLogAndAudit.contains(attributeDefName.getAttributeDefId());
507             boolean useSqlOnUpdate = (attributeDefNameIdIgnoreChangeLog || attributeDefIdIgnoreChangeLog)
508                 && GrouperConfig.retrieveConfig().propertyValueBoolean("grouperAllowSqlOnAttributeValueUpdate", true);
509             
510             // delete and re-add the row if values change
511             boolean someValueFieldIsDifferent = true;
512             
513             if (AttributeAssignValue.this.dbVersion() != null) {
514               someValueFieldIsDifferent = AttributeAssignValue.this.dbVersionDifferentFields().contains(FIELD_VALUE_INTEGER) ||
515                   AttributeAssignValue.this.dbVersionDifferentFields().contains(FIELD_VALUE_FLOATING) ||
516                   AttributeAssignValue.this.dbVersionDifferentFields().contains(FIELD_VALUE_STRING) ||
517                   AttributeAssignValue.this.dbVersionDifferentFields().contains(FIELD_VALUE_MEMBER_ID);
518             }
519 
520             if (!isInsert && useSqlOnUpdate) {
521               if (someValueFieldIsDifferent) {
522                 
523                 return HibernateSession.callbackHibernateSession(GrouperTransactionType.READ_WRITE_NEW, AuditControl.WILL_AUDIT, new HibernateHandler() {
524                   
525                   public Object callback(HibernateHandlerBean hibernateHandlerBean2) throws GrouperDAOException {
526                     
527                     HibernateSession hibernateSession = hibernateHandlerBean2.getHibernateSession();
528                     
529                     BySql bySql = hibernateSession.bySql();
530 
531                     String sql = "UPDATE grouper_attribute_assign_value SET hibernate_version_number = ?, context_id = ?, last_updated = ?, "
532                         + "value_integer = ?, value_floating = ?, value_string = ?, value_member_id = ? WHERE id = ?" ;
533                     List<Object> params = GrouperUtil.toListObject(AttributeAssignValue.this.getHibernateVersionNumber()+1, GrouperUuid.getUuid(), System.currentTimeMillis(),
534                         AttributeAssignValue.this.valueInteger, AttributeAssignValue.this.valueFloating, AttributeAssignValue.this.valueString, 
535                         AttributeAssignValue.this.valueMemberId, AttributeAssignValue.this.id);
536                     List<Type> types = HibUtils.listType(LongType.INSTANCE, StringType.INSTANCE, LongType.INSTANCE, LongType.INSTANCE, DoubleType.INSTANCE,
537                         StringType.INSTANCE, StringType.INSTANCE, StringType.INSTANCE);
538                     int rows = bySql.executeSql(sql, params, types);
539                     if (rows != 1) {
540                       if (rows == 0) {
541                         //try not to get errors...
542                         LOG.warn("NON-FATAL warning: Update attribute value gave 0 rows???? id: " + AttributeAssignValue.this.id
543                             + ", valueString: '" + AttributeAssignValue.this.valueString  + "', valueInteger: '" 
544                             + AttributeAssignValue.this.valueInteger  + "', valueFloating: '" 
545                             + AttributeAssignValue.this.valueFloating   + "', valueMemberId: '" 
546                             + AttributeAssignValue.this.valueMemberId + "'");
547                       } else {
548                         throw new RuntimeException("Why is rows not 1? " + rows + ", id: " + AttributeAssignValue.this.id
549                             + ", valueString: '" + AttributeAssignValue.this.valueString  + "', valueInteger: '" 
550                             + AttributeAssignValue.this.valueInteger  + "', valueFloating: '" 
551                             + AttributeAssignValue.this.valueFloating   + "', valueMemberId: '" 
552                             + AttributeAssignValue.this.valueMemberId + "'");
553                       }
554                     }
555                     AttributeAssignValue.this.dbVersionReset();
556                     
557                     // this is only for testing
558                     testingUseSqlCount++;
559                     
560                     return null;
561                   }
562                 });
563 
564               }
565               //nothing changed, and using SQL, just ignore
566               return null;
567             }
568             
569             if (!isInsert) {
570               
571               if (someValueFieldIsDifferent) {
572                
573                 
574                 if (attribute != null) {
575                   isLegacyAttributeUpdate = true;
576                 }
577                 
578                 if (isLegacyAttributeUpdate) {
579                   GrouperHooksUtils.callHooksIfRegistered(attribute, GrouperHookType.ATTRIBUTE,
580                       AttributeHooks.METHOD_ATTRIBUTE_PRE_UPDATE, HooksAttributeBean.class,
581                       attribute, Attribute.class, VetoTypeGrouper.ATTRIBUTE_PRE_UPDATE, false, false);
582                 }
583                 
584                 GrouperDAOFactory.getFactory().getAttributeAssignValue().delete(AttributeAssignValue.this);
585                 AttributeAssignValue.this.id = GrouperUuid.getUuid();
586                 AttributeAssignValue.this.createdOnDb = null;
587                 AttributeAssignValue.this.lastUpdatedDb = null;
588                 AttributeAssignValue.this.setHibernateVersionNumber(-1L);
589               }
590             }
591     
592             GrouperDAOFactory.getFactory().getAttributeAssignValue().saveOrUpdate(AttributeAssignValue.this);
593             
594             if (isLegacyAttributeUpdate) {
595               GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.ATTRIBUTE,
596                   AttributeHooks.METHOD_ATTRIBUTE_POST_COMMIT_UPDATE, HooksAttributeBean.class,
597                   attribute, Attribute.class);
598 
599               GrouperHooksUtils.callHooksIfRegistered(attribute, GrouperHookType.ATTRIBUTE,
600                   AttributeHooks.METHOD_ATTRIBUTE_POST_UPDATE, HooksAttributeBean.class,
601                   attribute, Attribute.class, VetoTypeGrouper.ATTRIBUTE_POST_UPDATE, true, false);
602             }
603             
604             if (!GrouperConfig.retrieveConfig().attributeDefIdsToIgnoreChangeLogAndAudit().contains(attributeDefName.getAttributeDefId()) && 
605                 !GrouperConfig.retrieveConfig().attributeDefNameIdsToIgnoreChangeLogAndAudit().contains(attributeDefName.getId())) {
606               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
607                 AuditEntryer/audit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(
608                     isInsert ? AuditTypeBuiltin.ATTRIBUTE_ASSIGN_VALUE_ADD : AuditTypeBuiltin.ATTRIBUTE_ASSIGN_VALUE_UPDATE, 
609                         "id", 
610                     AttributeAssignValue.this.getId(), "attributeAssignId", AttributeAssignValue.this.getAttributeAssignId(), 
611                     "attributeDefNameId", attributeAssign.getAttributeDefNameId(), 
612                     "value", AttributeAssignValue.this.valueString(), "attributeDefNameName", attributeDefName.getName());
613     
614                 if (isInsert) {
615                   
616                   auditEntry.setDescription("Added attribute assignment value");
617     
618                 } else {
619     
620                   auditEntry.setDescription("Updated attribute assignment value: " + differences);
621                   
622                 }
623                 auditEntry.saveOrUpdate(true);
624               }
625             }
626             
627             return null;
628           }
629         });
630             
631   }
632   
633   /**
634    * context id of the transaction
635    * @return context id
636    */
637   public String getContextId() {
638     return this.contextId;
639   }
640 
641   /**
642    * context id of the transaction
643    * @param contextId1
644    */
645   public void setContextId(String contextId1) {
646     this.contextId = contextId1;
647   }
648 
649   /**
650    * id of this attribute def
651    * @return id
652    */
653   public String getId() {
654     return this.id;
655   }
656 
657   /**
658    * id of this attribute def
659    * @param id1
660    */
661   public void setId(String id1) {
662     this.id = id1;
663   }
664 
665   
666   /**
667    * when last updated
668    * @return timestamp
669    */
670   public Timestamp getLastUpdated() {
671     return this.lastUpdatedDb == null ? null : new Timestamp(this.lastUpdatedDb);
672   }
673 
674   /**
675    * when last updated
676    * @return timestamp
677    */
678   public Long getLastUpdatedDb() {
679     return this.lastUpdatedDb;
680   }
681 
682   /**
683    * when last updated
684    * @param lastUpdated1
685    */
686   public void setLastUpdated(Timestamp lastUpdated1) {
687     this.lastUpdatedDb = lastUpdated1 == null ? null : lastUpdated1.getTime();
688   }
689 
690   /**
691    * when last updated
692    * @param lastUpdated1
693    */
694   public void setLastUpdatedDb(Long lastUpdated1) {
695     this.lastUpdatedDb = lastUpdated1;
696   }
697   
698   /**
699    * when created
700    * @return timestamp
701    */
702   public Timestamp getCreatedOn() {
703     return this.createdOnDb == null ? null : new Timestamp(this.createdOnDb);
704   }
705 
706   /**
707    * when created
708    * @return timestamp
709    */
710   public Long getCreatedOnDb() {
711     return this.createdOnDb;
712   }
713 
714   /**
715    * when created
716    * @param createdOn1
717    */
718   public void setCreatedOn(Timestamp createdOn1) {
719     this.createdOnDb = createdOn1 == null ? null : createdOn1.getTime();
720   }
721 
722   /**
723    * when created
724    * @param createdOn1
725    */
726   public void setCreatedOnDb(Long createdOn1) {
727     this.createdOnDb = createdOn1;
728   }
729 
730   /**
731    * attribute assignment in this value assignment
732    * @return the attributeNameId
733    */
734   public String getAttributeAssignId() {
735     return this.attributeAssignId;
736   }
737 
738   /**
739    * get the attribute assign
740    * @return the attribute assign
741    */
742   public AttributeAssign getAttributeAssign() {
743     
744     if (this.attributeAssign != null) {
745       return this.attributeAssign;
746     }
747     
748     if (StringUtils.isBlank(this.attributeAssignId)) {
749       return null;
750     }
751     //hopefully this is cached
752     this.attributeAssign = GrouperDAOFactory.getFactory().getAttributeAssign().findById(this.attributeAssignId, true);
753     return this.attributeAssign;
754   }
755   
756   /**
757    * attribute assignment in this value assignment
758    * @param attributeAssignId1 the attributeNameId to set
759    */
760   public void setAttributeAssignId(String attributeAssignId1) {
761     this.attributeAssignId = attributeAssignId1;
762     this.attributeAssign = null;
763   }
764   
765   /**
766    * string value
767    * @return the valueString
768    */
769   public String getValueString() {
770     return this.valueString;
771   }
772 
773   
774   /**
775    * string value
776    * @param valueString1 the valueString to set
777    */
778   public void setValueString(String valueString1) {
779     this.valueString = valueString1;
780   }
781 
782   
783   /**
784    * integer value
785    * @return the valueInteger
786    */
787   public Long getValueInteger() {
788     return this.valueInteger;
789   }
790 
791   
792   /**
793    * integer value
794    * @param valueInteger1 the valueInteger to set
795    */
796   public void setValueInteger(Long valueInteger1) {
797     this.valueInteger = valueInteger1;
798   }
799 
800   
801   /**
802    * memberId value (for subjects)
803    * @return the valueMemberId
804    */
805   public String getValueMemberId() {
806     return this.valueMemberId;
807   }
808 
809   
810   /**
811    * memberId value (for subjects)
812    * @param valueMemberId1 the valueMemberId to set
813    */
814   public void setValueMemberId(String valueMemberId1) {
815     this.valueMemberId = valueMemberId1;
816   }
817 
818   /**
819    * convert to xml bean for export
820    * @param grouperVersion
821    * @return xml bean
822    */
823   public XmlExportAttributeAssignValue xmlToExportAttributeAssignValue(GrouperVersion grouperVersion) {
824     
825     if (grouperVersion == null) {
826       throw new RuntimeException();
827     }
828     
829     XmlExportAttributeAssignValueValue.html#XmlExportAttributeAssignValue">XmlExportAttributeAssignValue xmlExportAttributeAssignValue = new XmlExportAttributeAssignValue(); 
830     
831     xmlExportAttributeAssignValue.setAttributeAssignId(this.getAttributeAssignId());
832     xmlExportAttributeAssignValue.setContextId(this.getContextId());
833     xmlExportAttributeAssignValue.setCreateTime(GrouperUtil.dateStringValue(this.getCreatedOnDb()));
834     xmlExportAttributeAssignValue.setHibernateVersionNumber(this.getHibernateVersionNumber());
835     xmlExportAttributeAssignValue.setModifierTime(GrouperUtil.dateStringValue(this.getLastUpdatedDb()));
836     xmlExportAttributeAssignValue.setUuid(this.getId());
837     xmlExportAttributeAssignValue.setValueInteger(this.getValueInteger());
838     xmlExportAttributeAssignValue.setValueMemberId(this.getValueMemberId());
839     xmlExportAttributeAssignValue.setValueString(this.getValueString());
840     
841     return xmlExportAttributeAssignValue;
842   }
843 
844   /**
845    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableMultiple#xmlRetrieveByIdOrKey(java.util.Collection)
846    */
847   public AttributeAssignValue xmlRetrieveByIdOrKey(Collection<String> idsToIgnore) {
848     return GrouperDAOFactory.getFactory().getAttributeAssignValue().findByUuidOrKey(idsToIgnore,
849         this.id, this.attributeAssignId, false, this.valueInteger, this.valueMemberId, this.valueString);
850   }
851 
852   /**
853    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlCopyBusinessPropertiesToExisting(java.lang.Object)
854    */
855   public void xmlCopyBusinessPropertiesToExisting(AttributeAssignValue existingRecord) {
856     existingRecord.setAttributeAssignId(this.attributeAssignId);
857     existingRecord.setId(this.id);
858     existingRecord.setValueInteger(this.valueInteger);
859     existingRecord.setValueMemberId(this.valueMemberId);
860     existingRecord.setValueString(this.valueString);
861   }
862 
863   /**
864    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlDifferentBusinessProperties(java.lang.Object)
865    */
866   public boolean xmlDifferentBusinessProperties(AttributeAssignValue other) {
867     if (!StringUtils.equals(this.attributeAssignId, other.attributeAssignId)) {
868       return true;
869     }
870     if (!StringUtils.equals(this.id, other.id)) {
871       return true;
872     }
873     if (!GrouperUtil.equals(this.valueInteger, other.valueInteger)) {
874       return true;
875     }
876     if (!StringUtils.equals(this.valueMemberId, other.valueMemberId)) {
877       return true;
878     }
879     if (!StringUtils.equals(this.valueString, other.valueString)) {
880       return true;
881     }
882     return false;
883   }
884 
885   /**
886    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlDifferentUpdateProperties(java.lang.Object)
887    */
888   public boolean xmlDifferentUpdateProperties(AttributeAssignValue other) {
889     if (!StringUtils.equals(this.contextId, other.contextId)) {
890       return true;
891     }
892     if (!GrouperUtil.equals(this.createdOnDb, other.createdOnDb)) {
893       return true;
894     }
895     if (!GrouperUtil.equals(this.getHibernateVersionNumber(), other.getHibernateVersionNumber())) {
896       return true;
897     }
898     if (!GrouperUtil.equals(this.lastUpdatedDb, other.lastUpdatedDb)) {
899       return true;
900     }
901     return false;
902   }
903 
904   /**
905    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlSaveBusinessProperties(java.lang.Object)
906    */
907   public AttributeAssignValue2/middleware/grouper/attr/value/AttributeAssignValue.html#AttributeAssignValue">AttributeAssignValue xmlSaveBusinessProperties(AttributeAssignValue existingRecord) {
908     //if its an insert, call the business method
909     if (existingRecord == null) {
910       //TODO user business method once it exists
911       existingRecord = new AttributeAssignValue();
912       existingRecord.setId(this.id);
913       existingRecord.setAttributeAssignId(this.attributeAssignId);
914       existingRecord.setValueInteger(this.valueInteger);
915       existingRecord.setValueMemberId(this.valueMemberId);
916       existingRecord.setValueString(this.valueString);
917       existingRecord.saveOrUpdate();
918     }
919 
920     this.xmlCopyBusinessPropertiesToExisting(existingRecord);
921     //if its an insert or update, then do the rest of the fields
922     existingRecord.saveOrUpdate();
923     return existingRecord;
924   }
925 
926   /**
927    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlSaveUpdateProperties()
928    */
929   public void xmlSaveUpdateProperties() {
930     GrouperDAOFactory.getFactory().getAttributeAssignValue().saveUpdateProperties(this);
931   }
932   
933   /**
934    * delete this record
935    */
936   public void delete() {
937     try {
938       HibernateSession.callbackHibernateSession(
939           GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT,
940           new HibernateHandler() {
941   
942             public Object callback(HibernateHandlerBean hibernateHandlerBean)
943                 throws GrouperDAOException {
944   
945               hibernateHandlerBean.getHibernateSession().setCachingEnabled(false);
946   
947                 GrouperDAOFactory.getFactory().getAttributeAssignValue().delete(AttributeAssignValue.this);
948   
949                 if (!GrouperConfig.retrieveConfig().attributeDefIdsToIgnoreChangeLogAndAudit().contains(
950                     AttributeAssignValue.this.getAttributeAssign().getAttributeDefName().getAttributeDefId()) && 
951                     !GrouperConfig.retrieveConfig().attributeDefNameIdsToIgnoreChangeLogAndAudit().contains(
952                         AttributeAssignValue.this.getAttributeAssign().getAttributeDefName().getId())) {
953                   if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
954                     AttributeAssign attributeAssign = AttributeAssignValue.this.getAttributeAssign();
955                     AttributeDefName attributeDefName = attributeAssign.getAttributeDefName();
956                     AuditEntryer/audit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.ATTRIBUTE_ASSIGN_VALUE_DELETE, 
957                             "id", 
958                         AttributeAssignValue.this.getId(), "attributeAssignId", AttributeAssignValue.this.getAttributeAssignId(), 
959                         "attributeDefNameId", attributeAssign.getAttributeDefNameId(), 
960                         "value", AttributeAssignValue.this.valueString(), "attributeDefNameName", attributeDefName.getName());
961                     auditEntry.setDescription("Deleted attributeAssignValue: " + AttributeAssignValue.this.getId());
962                     auditEntry.saveOrUpdate(true);
963                   }
964                 }
965                 return null;
966           }});
967     } catch (RuntimeException e) {
968       GrouperUtil.injectInException(e, " Problem deleting attribute assignValue: " + this + " ");
969       throw e;
970     }
971   }
972 
973   /**
974    * @see Object#toString()
975    */
976   @Override
977   public String toString() {
978     return "AttributeAssignValue.id#" + this.getId();
979   }
980 
981   /**
982    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlGetId()
983    */
984   public String xmlGetId() {
985     return this.getId();
986   }
987 
988   /**
989    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlSetId(java.lang.String)
990    */
991   public void xmlSetId(String theId) {
992     this.setId(theId);
993   }
994 
995   /**
996    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlToString()
997    */
998   public String xmlToString() {
999     StringWriter stringWriter = new StringWriter();
1000     
1001     stringWriter.write("AttributeAssignValue: " + this.getId());
1002 
1003 //    XmlExportUtils.toStringAttributeAssignValue(stringWriter, this, false);
1004     
1005     return stringWriter.toString();
1006     
1007   }
1008 
1009   /**
1010    * if the argument has the same value as this
1011    * @param attributeAssignValue
1012    * @return if the argument has the same value as this
1013    */
1014   public boolean sameValue(AttributeAssignValue attributeAssignValue) {
1015     if (attributeAssignValue == null) {
1016       return false;
1017     }
1018     if (!GrouperUtil.equals(this.valueMemberId, attributeAssignValue.valueMemberId)) {
1019       return false;
1020     }
1021     if (!GrouperUtil.equals(this.valueString, attributeAssignValue.valueString)) {
1022       return false;
1023     }
1024     if (!GrouperUtil.equals(this.valueFloating, attributeAssignValue.valueFloating)) {
1025       return false;
1026     }
1027     if (!GrouperUtil.equals(this.valueInteger, attributeAssignValue.valueInteger)) {
1028       return false;
1029     }
1030     return true;
1031   }
1032  
1033   /**
1034    * 
1035    */
1036   public static enum AttributeAssignValueType {
1037     
1038     /** has an integer value */
1039     integerValue {
1040 
1041       /**
1042        * 
1043        * @see edu.internet2.middleware.grouper.attr.value.AttributeAssignValue.AttributeAssignValueType#compatibleWith(edu.internet2.middleware.grouper.attr.AttributeDefValueType)
1044        */
1045       @Override
1046       public boolean compatibleWith(AttributeDefValueType attributeDefValueType) {
1047         return attributeDefValueType == AttributeDefValueType.integer 
1048           || attributeDefValueType == AttributeDefValueType.timestamp;
1049       }
1050       
1051     },
1052     
1053     /** has a floating value */
1054     floating {
1055 
1056       /**
1057        * 
1058        * @see edu.internet2.middleware.grouper.attr.value.AttributeAssignValue.AttributeAssignValueType#compatibleWith(edu.internet2.middleware.grouper.attr.AttributeDefValueType)
1059        */
1060       @Override
1061       public boolean compatibleWith(AttributeDefValueType attributeDefValueType) {
1062         return attributeDefValueType == AttributeDefValueType.floating;
1063       }
1064       
1065     },
1066     
1067     /** has a string value */
1068     string {
1069 
1070       /**
1071        * 
1072        * @see edu.internet2.middleware.grouper.attr.value.AttributeAssignValue.AttributeAssignValueType#compatibleWith(edu.internet2.middleware.grouper.attr.AttributeDefValueType)
1073        */
1074       @Override
1075       public boolean compatibleWith(AttributeDefValueType attributeDefValueType) {
1076         return attributeDefValueType == AttributeDefValueType.string;
1077       }
1078       
1079     },
1080     
1081     /** has a member id */
1082     memberId {
1083 
1084       /**
1085        * 
1086        * @see edu.internet2.middleware.grouper.attr.value.AttributeAssignValue.AttributeAssignValueType#compatibleWith(edu.internet2.middleware.grouper.attr.AttributeDefValueType)
1087        */
1088       @Override
1089       public boolean compatibleWith(AttributeDefValueType attributeDefValueType) {
1090         return attributeDefValueType == AttributeDefValueType.memberId;
1091       }
1092       
1093     },
1094     
1095     /** doesnt have a value */
1096     nullValue {
1097 
1098       /**
1099        * 
1100        * @see edu.internet2.middleware.grouper.attr.value.AttributeAssignValue.AttributeAssignValueType#compatibleWith(edu.internet2.middleware.grouper.attr.AttributeDefValueType)
1101        */
1102       @Override
1103       public boolean compatibleWith(AttributeDefValueType attributeDefValueType) {
1104         //this is ok for all types
1105         return true;
1106       }
1107       
1108     },
1109     
1110     /** has multi values, thats bad */
1111     multiValueError {
1112 
1113       /**
1114        * 
1115        * @see edu.internet2.middleware.grouper.attr.value.AttributeAssignValue.AttributeAssignValueType#compatibleWith(edu.internet2.middleware.grouper.attr.AttributeDefValueType)
1116        */
1117       @Override
1118       public boolean compatibleWith(AttributeDefValueType attributeDefValueType) {
1119         //this is bad
1120         return false;
1121       }
1122       
1123     };
1124     
1125     /**
1126      * 
1127      * @param attributeDefValueType
1128      * @return true if the value type is compatible with the def type
1129      */
1130     public abstract boolean compatibleWith(AttributeDefValueType attributeDefValueType);
1131     
1132   }
1133 
1134   /**
1135    * get the value whatever it is or null if none
1136    * @return the value
1137    */
1138   public Object getValue() {
1139     if (!StringUtils.isEmpty(this.valueString)) {
1140       return this.valueString;
1141     }
1142     if (this.valueInteger != null) {
1143       return this.valueInteger;
1144     }
1145     if (this.valueFloating != null) {
1146       return this.valueFloating;
1147     }
1148     if (this.valueMemberId != null) {
1149       return this.valueMemberId;
1150     }
1151     //probably null :)
1152     return null;
1153   }
1154   
1155   /**
1156    * get the type of this value
1157    * @return the type of this value
1158    */
1159   public AttributeAssignValueType getCurrentAssignValueType() {
1160     int valueCount = 0;
1161     valueCount += this.valueFloating != null ? 1 : 0;
1162     valueCount += this.valueInteger != null ? 1 : 0;
1163     valueCount += this.valueMemberId != null ? 1 : 0;
1164     valueCount += !StringUtils.isEmpty(this.valueString) ? 1 : 0;
1165     if (valueCount > 1) {
1166       return AttributeAssignValueType.multiValueError;
1167     }
1168     if (valueCount == 0) {
1169       return AttributeAssignValueType.nullValue;
1170     }
1171     if (!StringUtils.isEmpty(this.valueString)) {
1172       return AttributeAssignValueType.string;
1173     }
1174     if (this.valueInteger != null) {
1175       return AttributeAssignValueType.integerValue;
1176     }
1177     if (this.valueFloating != null) {
1178       return AttributeAssignValueType.floating;
1179     }
1180     if (this.valueMemberId != null) {
1181       return AttributeAssignValueType.memberId;
1182     }
1183     throw new RuntimeException("Why are we here? " + this);
1184   }
1185   
1186   /**
1187    * @see edu.internet2.middleware.grouper.GrouperAPI#onPostDelete(edu.internet2.middleware.grouper.hibernate.HibernateSession)
1188    */
1189   @Override
1190   public void onPostDelete(HibernateSession hibernateSession) {
1191     super.onPostDelete(hibernateSession);
1192   
1193     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.ATTRIBUTE_ASSIGN_VALUE, 
1194         AttributeAssignValueHooks.METHOD_ATTRIBUTE_ASSIGN_VALUE_POST_COMMIT_DELETE, HooksAttributeAssignValueBean.class, 
1195         this, AttributeAssignValue.class);
1196   
1197     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.ATTRIBUTE_ASSIGN_VALUE, 
1198         AttributeAssignValueHooks.METHOD_ATTRIBUTE_ASSIGN_VALUE_POST_DELETE, HooksAttributeAssignValueBean.class, 
1199         this, AttributeAssignValue.class, VetoTypeGrouper.ATTRIBUTE_ASSIGN_VALUE_POST_DELETE, false, true);
1200   
1201     Attribute attribute = Attribute.internal_getAttribute(this, null, false);
1202     if (attribute != null) {
1203       // this is a legacy attribute
1204       GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.ATTRIBUTE,
1205           AttributeHooks.METHOD_ATTRIBUTE_POST_COMMIT_DELETE, HooksAttributeBean.class,
1206           attribute, Attribute.class);
1207 
1208       GrouperHooksUtils.callHooksIfRegistered(attribute, GrouperHookType.ATTRIBUTE,
1209           AttributeHooks.METHOD_ATTRIBUTE_POST_DELETE, HooksAttributeBean.class,
1210           attribute, Attribute.class, VetoTypeGrouper.ATTRIBUTE_POST_DELETE, false, true);
1211     }
1212   }
1213 
1214   /**
1215    * @see edu.internet2.middleware.grouper.hibernate.HibGrouperLifecycle#onPostSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
1216    */
1217   @Override
1218   public void onPostSave(HibernateSession hibernateSession) {
1219   
1220     super.onPostSave(hibernateSession);
1221     
1222     //if limit, then validate
1223     this.validateLimit();
1224 
1225     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.ATTRIBUTE_ASSIGN_VALUE, 
1226         AttributeAssignValueHooks.METHOD_ATTRIBUTE_ASSIGN_VALUE_POST_INSERT, HooksAttributeAssignValueBean.class, 
1227         this, AttributeAssignValue.class, VetoTypeGrouper.ATTRIBUTE_ASSIGN_VALUE_POST_INSERT, true, false);
1228   
1229     //do these second so the right object version is set, and dbVersion is ok
1230     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.ATTRIBUTE_ASSIGN_VALUE, 
1231         AttributeAssignValueHooks.METHOD_ATTRIBUTE_ASSIGN_VALUE_POST_COMMIT_INSERT, HooksAttributeAssignValueBean.class, 
1232         this, AttributeAssignValue.class);
1233   
1234     Attribute attribute = Attribute.internal_getAttribute(this, null, false);
1235     if (attribute != null) {
1236       // this is a legacy attribute
1237       GrouperHooksUtils.callHooksIfRegistered(attribute, GrouperHookType.ATTRIBUTE,
1238           AttributeHooks.METHOD_ATTRIBUTE_POST_INSERT, HooksAttributeBean.class,
1239           attribute, Attribute.class, VetoTypeGrouper.ATTRIBUTE_POST_INSERT, true, false);
1240 
1241       GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.ATTRIBUTE,
1242           AttributeHooks.METHOD_ATTRIBUTE_POST_COMMIT_INSERT, HooksAttributeBean.class,
1243           attribute, Attribute.class);
1244     }
1245   }
1246 
1247   /**
1248    * @see edu.internet2.middleware.grouper.hibernate.HibGrouperLifecycle#onPostUpdate(HibernateSession)
1249    */
1250   public void onPostUpdate(HibernateSession hibernateSession) {
1251     
1252     super.onPostUpdate(hibernateSession);
1253     
1254     this.setLastUpdatedDb(System.currentTimeMillis());
1255 
1256     //if limit, then validate
1257     this.validateLimit();
1258     
1259     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.ATTRIBUTE_ASSIGN_VALUE, 
1260         AttributeAssignValueHooks.METHOD_ATTRIBUTE_ASSIGN_VALUE_POST_COMMIT_UPDATE, HooksAttributeAssignValueBean.class, 
1261         this, AttributeAssignValue.class);
1262   
1263     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.ATTRIBUTE_ASSIGN_VALUE, 
1264         AttributeAssignValueHooks.METHOD_ATTRIBUTE_ASSIGN_VALUE_POST_UPDATE, HooksAttributeAssignValueBean.class, 
1265         this, AttributeAssignValue.class, VetoTypeGrouper.ATTRIBUTE_ASSIGN_VALUE_POST_UPDATE, true, false);
1266   }
1267 
1268   /**
1269    * 
1270    */
1271   private void validateLimit() {
1272     
1273     AttributeAssign limitAssign = this.getAttributeAssign();
1274     if (limitAssign.getAttributeDef().getAttributeDefType() == AttributeDefType.limit) {
1275       
1276       String valueFriendly = this.getValueFriendly();
1277       
1278       //we dont validate blank values
1279       if (!StringUtils.isBlank(valueFriendly)) {
1280         AttributeAssignValueuteAssignValue.html#AttributeAssignValue">AttributeAssignValue attributeAssignValue = new AttributeAssignValue();
1281         attributeAssignValue.setAttributeAssignId(limitAssign.getId());
1282         
1283         AttributeDefName limit = limitAssign.getAttributeDefName();
1284         String limitName = limit.getName();
1285         PermissionLimitInterface permissionLimitInterface = PermissionLimitUtils.logicInstance(limitName);
1286 
1287         Set<AttributeAssignValue> limitAssignValues = limitAssign.getValueDelegate().retrieveValues();
1288         
1289         if (GrouperUtil.length(limitAssignValues) == 0) {
1290           throw new RuntimeException("Why are there no values?");
1291         }
1292         
1293         PermissionLimitDocumentation error = permissionLimitInterface.validateLimitAssignValue(limitAssign, limitAssignValues);
1294         if (error != null) {
1295           
1296           throw new LimitInvalidException("Invalid limit", error);
1297         }
1298       }
1299       
1300     }
1301     
1302   }
1303   
1304   /**
1305    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreDelete(edu.internet2.middleware.grouper.hibernate.HibernateSession)
1306    */
1307   @Override
1308   public void onPreDelete(HibernateSession hibernateSession) {
1309     super.onPreDelete(hibernateSession);
1310 
1311     if (!GrouperConfig.retrieveConfig().attributeDefIdsToIgnoreChangeLogAndAudit()
1312         .contains(this.getAttributeAssign().getAttributeDefName().getAttributeDefId()) && 
1313         !GrouperConfig.retrieveConfig().attributeDefNameIdsToIgnoreChangeLogAndAudit()
1314         .contains(this.getAttributeAssign().getAttributeDefName().getId())) {
1315       
1316       new ChangeLogEntry(true, ChangeLogTypeBuiltin.ATTRIBUTE_ASSIGN_VALUE_DELETE, 
1317           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_DELETE.id.name(), this.getId(), 
1318           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_DELETE.attributeAssignId.name(), this.getAttributeAssignId(), 
1319           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_DELETE.attributeDefNameId.name(), this.getAttributeAssign().getAttributeDefNameId(), 
1320           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_DELETE.attributeDefNameName.name(), this.getAttributeAssign().getAttributeDefName().getName(),
1321           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_DELETE.value.name(), this.dbVersion() == null ? null : this.dbVersion().valueString(),
1322           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_DELETE.valueType.name(), this.getAttributeAssign().getAttributeDef().getValueType().name()).save();
1323     }
1324     
1325     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.ATTRIBUTE_ASSIGN_VALUE, 
1326         AttributeAssignValueHooks.METHOD_ATTRIBUTE_ASSIGN_VALUE_PRE_DELETE, HooksAttributeAssignValueBean.class, 
1327         this, AttributeAssignValue.class, VetoTypeGrouper.ATTRIBUTE_ASSIGN_VALUE_PRE_DELETE, false, false);
1328     
1329     Attribute attribute = Attribute.internal_getAttribute(this, null, false);
1330     if (attribute != null) {
1331       // this is a legacy attribute
1332       
1333       GrouperHooksUtils.callHooksIfRegistered(attribute, GrouperHookType.ATTRIBUTE,
1334           AttributeHooks.METHOD_ATTRIBUTE_PRE_DELETE, HooksAttributeBean.class,
1335           attribute, Attribute.class, VetoTypeGrouper.ATTRIBUTE_PRE_DELETE, false, false);
1336     }
1337   }
1338 
1339   /**
1340    * check that entity id does not exist
1341    */
1342   private void checkEntityValidations() {
1343     
1344     //if this is an entity id
1345     AttributeAssign theAttributeAssign = this.getAttributeAssign();
1346     if (StringUtils.equals(theAttributeAssign.getAttributeDefName().getName(), EntityUtils.entitySubjectIdentifierName())) {
1347       
1348       if (StringUtils.isBlank(this.valueString)) {
1349         throw new RuntimeException("valueString cannot be blank");
1350       }
1351 
1352       //make sure its the same folder as the entity
1353       Entity entity =  theAttributeAssign.getOwnerGroup();
1354       
1355       String folderNameWithColon = GrouperUtil.parentStemNameFromName(entity.getName()) + ":";
1356       
1357       if (!this.valueString.startsWith(folderNameWithColon)) {
1358         throw new RuntimeException("Value must start with the entity's folder name: '" + folderNameWithColon + "'" );
1359       }
1360       
1361       Set<AttributeAssignValue> attributeAssignValues = GrouperDAOFactory.getFactory().getAttributeAssignValue().findByValueString(this.valueString);
1362       
1363       for (AttributeAssignValue attributeAssignValue : GrouperUtil.nonNull(attributeAssignValues)) {
1364         
1365         if (!StringUtils.equals(this.id, attributeAssignValue.getId())) {
1366           throw new RuntimeException("Value is same as another entity subject id: " + this.id + ", " + attributeAssignValue.getId() + ": " + this.valueString);
1367         }
1368         
1369       }
1370       
1371     }
1372   }
1373   
1374   /**
1375    * 
1376    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
1377    */
1378   @Override
1379   public void onPreSave(HibernateSession hibernateSession) {
1380     super.onPreSave(hibernateSession);
1381 
1382     checkEntityValidations();
1383     
1384     long now = System.currentTimeMillis();
1385     if (this.createdOnDb == null) {
1386       this.setCreatedOnDb(now);
1387     }
1388     this.setLastUpdatedDb(now);
1389     
1390     if (!GrouperConfig.retrieveConfig().attributeDefIdsToIgnoreChangeLogAndAudit()
1391         .contains(this.getAttributeAssign().getAttributeDefName().getAttributeDefId()) && 
1392         !GrouperConfig.retrieveConfig().attributeDefNameIdsToIgnoreChangeLogAndAudit()
1393         .contains(this.getAttributeAssign().getAttributeDefName().getId())) {
1394       new ChangeLogEntry(true, ChangeLogTypeBuiltin.ATTRIBUTE_ASSIGN_VALUE_ADD, 
1395           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_ADD.id.name(), this.getId(), 
1396           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_ADD.attributeAssignId.name(), this.getAttributeAssignId(), 
1397           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_ADD.attributeDefNameId.name(), this.getAttributeAssign().getAttributeDefNameId(), 
1398           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_ADD.attributeDefNameName.name(), this.getAttributeAssign().getAttributeDefName().getName(),
1399           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_ADD.value.name(), this.valueString(),
1400           ChangeLogLabels.ATTRIBUTE_ASSIGN_VALUE_ADD.valueType.name(), this.getAttributeAssign().getAttributeDef().getValueType().name()).save();
1401     }
1402     
1403     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.ATTRIBUTE_ASSIGN_VALUE, 
1404         AttributeAssignValueHooks.METHOD_ATTRIBUTE_ASSIGN_VALUE_PRE_INSERT, HooksAttributeAssignValueBean.class, 
1405         this, AttributeAssignValue.class, VetoTypeGrouper.ATTRIBUTE_ASSIGN_VALUE_PRE_INSERT, false, false);
1406     
1407     Attribute attribute = Attribute.internal_getAttribute(this, null, false);
1408     if (attribute != null) {
1409       // this is a legacy attribute
1410       GrouperHooksUtils.callHooksIfRegistered(attribute, GrouperHookType.ATTRIBUTE,
1411           AttributeHooks.METHOD_ATTRIBUTE_PRE_INSERT, HooksAttributeBean.class,
1412           attribute, Attribute.class, VetoTypeGrouper.ATTRIBUTE_PRE_INSERT, false, false);
1413     }
1414   }
1415 
1416   /**
1417    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreUpdate(edu.internet2.middleware.grouper.hibernate.HibernateSession)
1418    */
1419   @Override
1420   public void onPreUpdate(HibernateSession hibernateSession) {
1421     super.onPreUpdate(hibernateSession);
1422     
1423     checkEntityValidations();
1424 
1425     this.setLastUpdatedDb(System.currentTimeMillis());
1426     
1427     if (AttributeAssignValue.this.dbVersionDifferentFields().contains(FIELD_VALUE_INTEGER) ||
1428         AttributeAssignValue.this.dbVersionDifferentFields().contains(FIELD_VALUE_FLOATING) ||
1429         AttributeAssignValue.this.dbVersionDifferentFields().contains(FIELD_VALUE_STRING) ||
1430         AttributeAssignValue.this.dbVersionDifferentFields().contains(FIELD_VALUE_MEMBER_ID)) {
1431       if (!Member.inMemberChangeSubject()) {
1432         throw new RuntimeException("Cannot update values.  Must delete and re-add db rows.");
1433       }
1434     }
1435     
1436     if (this.dbVersionDifferentFields().contains(FIELD_ATTRIBUTE_ASSIGN_ID)) {
1437       throw new RuntimeException("cannot update attributeAssignId");
1438     }
1439 
1440     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.ATTRIBUTE_ASSIGN_VALUE, 
1441         AttributeAssignValueHooks.METHOD_ATTRIBUTE_ASSIGN_VALUE_PRE_UPDATE, HooksAttributeAssignValueBean.class, 
1442         this, AttributeAssignValue.class, VetoTypeGrouper.ATTRIBUTE_ASSIGN_VALUE_PRE_UPDATE, false, false);
1443   }
1444 
1445   /**
1446    * save the state when retrieving from DB
1447    * @return the dbVersion
1448    */
1449   @Override
1450   public AttributeAssignValue dbVersion() {
1451     return (AttributeAssignValue)this.dbVersion;
1452   }
1453   
1454   /**
1455    * take a snapshot of the data since this is what is in the db
1456    */
1457   @Override
1458   public void dbVersionReset() {
1459     //lets get the state from the db so we know what has changed
1460     this.dbVersion = GrouperUtil.clone(this, DB_VERSION_FIELDS);
1461   }
1462 
1463 
1464   /**
1465    * @see edu.internet2.middleware.grouper.GrouperAPI#dbVersionDifferentFields()
1466    */
1467   @Override
1468   public Set<String> dbVersionDifferentFields() {
1469     if (this.dbVersion == null) {
1470       throw new RuntimeException("State was never stored from db");
1471     }
1472     //easier to unit test if everything is ordered
1473     Set<String> result = GrouperUtil.compareObjectFields(this, this.dbVersion,
1474         DB_VERSION_FIELDS, null);
1475     return result;
1476   }
1477   
1478   /**
1479    * set this for caching
1480    * @param attributeAssign1
1481    */
1482   public void internalSetAttributeAssign(AttributeAssign attributeAssign1) {
1483     
1484     if (attributeAssign1 != null) {
1485       if (!StringUtils.equals(this.attributeAssignId, attributeAssign1.getId())) {
1486         throw new RuntimeException("Why does the attributeAssign id " 
1487             + this.attributeAssignId + " not equal the param id: " + attributeAssign1.getId());
1488       }
1489     }
1490     
1491     this.attributeAssign = attributeAssign1;
1492   }
1493 }