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.io.StringWriter;
35  import java.util.Collection;
36  import java.util.HashSet;
37  import java.util.Set;
38  import java.util.TreeSet;
39  
40  import org.apache.commons.lang.StringUtils;
41  import org.apache.commons.lang.builder.EqualsBuilder;
42  import org.apache.commons.lang.builder.HashCodeBuilder;
43  import org.apache.commons.lang.builder.ToStringBuilder;
44  import org.apache.commons.lang.time.StopWatch;
45  import org.apache.commons.logging.Log;
46  
47  import edu.internet2.middleware.grouper.audit.AuditEntry;
48  import edu.internet2.middleware.grouper.audit.AuditTypeBuiltin;
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.exception.InsufficientPrivilegeException;
53  import edu.internet2.middleware.grouper.exception.SchemaException;
54  import edu.internet2.middleware.grouper.group.GroupSet;
55  import edu.internet2.middleware.grouper.hibernate.AuditControl;
56  import edu.internet2.middleware.grouper.hibernate.GrouperTransactionType;
57  import edu.internet2.middleware.grouper.hibernate.HibernateHandler;
58  import edu.internet2.middleware.grouper.hibernate.HibernateHandlerBean;
59  import edu.internet2.middleware.grouper.hibernate.HibernateSession;
60  import edu.internet2.middleware.grouper.hooks.FieldHooks;
61  import edu.internet2.middleware.grouper.hooks.beans.HooksFieldBean;
62  import edu.internet2.middleware.grouper.hooks.logic.GrouperHookType;
63  import edu.internet2.middleware.grouper.hooks.logic.GrouperHooksUtils;
64  import edu.internet2.middleware.grouper.hooks.logic.VetoTypeGrouper;
65  import edu.internet2.middleware.grouper.internal.dao.GrouperDAOException;
66  import edu.internet2.middleware.grouper.internal.dao.QueryOptions;
67  import edu.internet2.middleware.grouper.internal.dao.hib3.Hib3GrouperVersioned;
68  import edu.internet2.middleware.grouper.internal.util.GrouperUuid;
69  import edu.internet2.middleware.grouper.internal.util.Quote;
70  import edu.internet2.middleware.grouper.log.EventLog;
71  import edu.internet2.middleware.grouper.misc.E;
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.misc.M;
76  import edu.internet2.middleware.grouper.privs.Privilege;
77  import edu.internet2.middleware.grouper.util.GrouperUtil;
78  import edu.internet2.middleware.grouper.validator.AddFieldToGroupTypeValidator;
79  import edu.internet2.middleware.grouper.xml.export.XmlExportField;
80  import edu.internet2.middleware.grouper.xml.export.XmlImportable;
81  
82  
83  /** 
84   * Schema specification for a Group attribute or list.
85   * Reference to members list is: Group.getDefaultList()
86   * <p/>
87   * @author  blair christensen.
88   * @version $Id: Field.java,v 1.48 2009-09-24 18:07:16 shilen Exp $    
89   */
90  public class Field extends GrouperAPI implements Comparable<Field>, GrouperHasContext, Hib3GrouperVersioned, XmlImportable<Field> {
91  
92    /**
93     * get the fields that this field implies by inheritance
94     * @return the fields, note, dont change the list after you get it
95     */
96    public Collection<Field> getImpliedFields() {
97  
98      if (this.isPrivilege()) {
99        Privilege privilege = Privilege.listToPriv(this.name, true);
100       return Privilege.convertPrivilegesToFields(privilege.getImpliedPrivileges());
101     }
102     
103     throw new RuntimeException("Not expecting field: " + this.name);
104   }
105 
106   /**
107    * 
108    * @return if this field is a privilege
109    */
110   public boolean isPrivilege() {
111     
112     if (this.isAttributeDefListField() || this.isGroupAccessField() || this.isStemListField()) {
113       return true;
114     }
115     return false;
116   }
117 
118   /**
119    * return the uuid
120    * @return uuid
121    */
122   public String getId() {
123     return this.getUuid();
124   }
125   
126   /**
127    * see if there are inherited privileges to also include
128    * @return the inherited fields
129    */
130   public static Collection<Field> calculateInheritedPrivileges(Collection<Field> fields, boolean includeInheritedPrivileges) {
131     if (!includeInheritedPrivileges || GrouperUtil.length(fields) == 0) {
132       return fields;
133     }
134     
135     Set<Field> additionalFields = new HashSet<Field>();
136     
137     for (Field field : GrouperUtil.nonNull(fields)) {
138       
139       if (field.isAttributeDefListField() || field.isGroupAccessField() || field.isStemListField()) {
140         Privilege privilege = Privilege.listToPriv(field.getName(), true);
141         Collection<Privilege> privileges = privilege.getInheritedPrivileges();
142         Collection<Field> theFields = Privilege.convertPrivilegesToFields(privileges);
143         additionalFields.addAll(theFields);
144       }
145       
146     }
147     
148     return additionalFields;
149   }
150 
151   /** field name for creators */
152   public static final String FIELD_NAME_CREATORS = "creators";
153   
154   /** field name for stemmers */
155   public static final String FIELD_NAME_STEMMERS = "stemmers";
156   
157   /** field name for viewers */
158   public static final String FIELD_NAME_VIEWERS = "viewers";
159   
160   /** field name for attr viewers */
161   public static final String FIELD_NAME_ATTR_VIEWERS = "attrViewers";
162   
163   /** field name for admins */
164   public static final String FIELD_NAME_ADMINS = "admins";
165   
166   /** field name for attr admins */
167   public static final String FIELD_NAME_ATTR_ADMINS = "attrAdmins";
168   
169   /** field name for readers */
170   public static final String FIELD_NAME_READERS = "readers";
171   
172   /** field name for attr readers */
173   public static final String FIELD_NAME_ATTR_READERS = "attrReaders";
174   
175   /** field name for updaters */
176   public static final String FIELD_NAME_UPDATERS = "updaters";
177   
178   /** field name for attr updaters */
179   public static final String FIELD_NAME_ATTR_UPDATERS = "attrUpdaters";
180   
181   /** field name for optins */
182   public static final String FIELD_NAME_OPTINS = "optins";
183   
184   /** field name for attr optins */
185   public static final String FIELD_NAME_ATTR_OPTINS = "attrOptins";
186   
187   /** field name for optouts */
188   public static final String FIELD_NAME_OPTOUTS = "optouts";
189 
190   /** field name for attr optouts */
191   public static final String FIELD_NAME_ATTR_OPTOUTS = "attrOptouts";
192 
193   /** field name for groupAttrReaders */
194   public static final String FIELD_NAME_GROUP_ATTR_READERS = "groupAttrReaders";
195   
196   /** field name for groupAttrUpdaters */
197   public static final String FIELD_NAME_GROUP_ATTR_UPDATERS = "groupAttrUpdaters";
198 
199   /** field name for attrDefAttrReaders */
200   public static final String FIELD_NAME_ATTR_DEF_ATTR_READERS = "attrDefAttrReaders";
201   
202   /** field name for attrDefAttrUpdaters */
203   public static final String FIELD_NAME_ATTR_DEF_ATTR_UPDATERS = "attrDefAttrUpdaters";
204 
205   /** field name for stemAttrReaders */
206   public static final String FIELD_NAME_STEM_ATTR_READERS = "stemAttrReaders";
207   
208   /** field name for stemAttrUpdaters */
209   public static final String FIELD_NAME_STEM_ATTR_UPDATERS = "stemAttrUpdaters";
210   
211   /** field name for stemAdmins */
212   public static final String FIELD_NAME_STEM_ADMINS = "stemAdmins";
213   
214   /** col */
215   public static final String COLUMN_ID = "id";
216   
217   /** col */
218   public static final String COLUMN_NAME = "name";
219   
220   /** col */
221   public static final String COLUMN_READ_PRIVILEGE = "read_privilege";
222   
223   /** col */
224   public static final String COLUMN_TYPE = "type";
225   
226   /** col */
227   public static final String COLUMN_WRITE_PRIVILEGE = "write_privilege";
228   
229   /** col */
230   public static final String COLUMN_CONTEXT_ID = "context_id";
231   
232   /** logger */
233   private static final Log LOG = GrouperUtil.getLog(Field.class);
234   
235   /**
236    * print out a collection of fields
237    * @param collection
238    * @return the field names comma separated
239    */
240   public static String fieldNames(Collection<Field> collection) {
241     StringBuilder result = new StringBuilder();
242     for (Field field : GrouperUtil.nonNull(collection)) {
243       result.append(field.getName()).append(", ");
244     }
245     if (result.length() >= 2) {
246       //take off the last comma and space
247       result.delete(result.length()-2, result.length());
248     }
249     return result.toString();
250   }
251 
252   /** table name for fields */
253   public static final String TABLE_GROUPER_FIELDS = "grouper_fields";
254   
255   /** uuid col in db */
256   public static final String COLUMN_FIELD_UUID = "field_uuid";
257   
258   /** old id col for id conversion */
259   public static final String COLUMN_OLD_ID = "old_id";
260   
261   /** old uuid id col for id conversion */
262   public static final String COLUMN_OLD_FIELD_UUID = "old_field_uuid";
263   
264   
265   //*****  START GENERATED WITH GenerateFieldConstants.java *****//
266 
267   /** constant for field name for: dbVersion */
268   public static final String FIELD_DB_VERSION = "dbVersion";
269 
270   /** constant for field name for: name */
271   public static final String FIELD_NAME = "name";
272 
273   /** constant for field name for: readPrivilege */
274   public static final String FIELD_READ_PRIVILEGE = "readPrivilege";
275 
276   /** constant for field name for: type */
277   public static final String FIELD_TYPE = "type";
278 
279   /** constant for field name for: uuid */
280   public static final String FIELD_UUID = "uuid";
281 
282   /** constant for field name for: writePrivilege */
283   public static final String FIELD_WRITE_PRIVILEGE = "writePrivilege";
284 
285   /**
286    * fields which are included in db version
287    */
288   private static final Set<String> DB_VERSION_FIELDS = GrouperUtil.toSet(
289       FIELD_NAME, FIELD_READ_PRIVILEGE, 
290       FIELD_TYPE, FIELD_UUID, FIELD_WRITE_PRIVILEGE);
291 
292   /**
293    * fields which are included in clone method
294    */
295   private static final Set<String> CLONE_FIELDS = GrouperUtil.toSet(
296       FIELD_DB_VERSION, FIELD_HIBERNATE_VERSION_NUMBER, 
297       FIELD_NAME, FIELD_READ_PRIVILEGE, FIELD_TYPE, FIELD_UUID, 
298       FIELD_WRITE_PRIVILEGE);
299 
300   //*****  END GENERATED WITH GenerateFieldConstants.java *****//
301 
302   /** constant for property name for: uuid */
303   public static final String PROPERTY_UUID = "uuid";
304 
305 
306   /** context id of the transaction */
307   private String contextId;
308 
309   /**
310    * context id of the transaction
311    * @return context id
312    */
313   public String getContextId() {
314     return this.contextId;
315   }
316 
317   /**
318    * context id of the transaction
319    * @param contextId1
320    */
321   public void setContextId(String contextId1) {
322     this.contextId = contextId1;
323   }
324 
325   /** */
326   private String    name;
327   /** */
328   private String    readPrivilege;
329   /** */
330   private String    type;
331   /** */
332   private String    uuid;
333   /** */
334   private String    writePrivilege;
335   /** */
336   public  static final  long      serialVersionUID  = 2072790175332537149L;
337 
338 
339   /**
340    * see if this is a list of members field for stems
341    * @return true if stem list field
342    */
343   public boolean isStemListField() {
344     return StringUtils.equals("naming", this.type);
345   }
346   
347   /**
348    * see if this is a list of members field for attributeDefs
349    * @return true if attribute def list field
350    */
351   public boolean isAttributeDefListField() {
352     return StringUtils.equals("attributeDef", this.type);
353   }
354   
355   /**
356    * see if this is a list of members field for groups
357    * @return true if group list field
358    */
359   public boolean isGroupListField() {
360     return StringUtils.equals("list", this.type)
361       || StringUtils.equals("access", this.type);
362   }
363   
364   /**
365    * see if this is privilege field for groups
366    * @return true if group access list field
367    */
368   public boolean isGroupAccessField() {
369     return StringUtils.equals("access", this.type);
370   }
371   
372   /**
373    * see if this is a list of members field for groups
374    * @return true if group list field
375    */
376   public boolean isEntityListField() {
377     return StringUtils.equals("access", this.type)
378       && (StringUtils.equals(Field.FIELD_NAME_ADMINS, this.name)
379           || StringUtils.equals(Field.FIELD_NAME_VIEWERS, this.name)
380           || StringUtils.equals(Field.FIELD_NAME_GROUP_ATTR_READERS, this.name)
381           || StringUtils.equals(Field.FIELD_NAME_GROUP_ATTR_UPDATERS, this.name));
382   }
383   
384   /**
385    * @return field type
386    */
387   public FieldType getType() {
388     return FieldType.getInstance( this.getTypeString() );
389   }
390 
391   /**
392    * @return privilege
393    */
394   public Privilege getReadPriv() {
395     return Privilege.getInstance( this.getReadPrivilege() ); 
396   } // public Privilege getReadPriv()
397 
398   
399   /**
400    * @return privilege
401    */
402   public Privilege getWritePriv() {
403     return Privilege.getInstance( this.getWritePrivilege() );
404   } // public Privilege getWritePriv()
405 
406   /**
407    * @param other 
408    * @return if equals
409    * @since   1.2.0
410    */
411   public boolean equals(Object other) {
412     if (this == other) { 
413       return true;
414     }
415     if ( !(other instanceof Field) ) {
416       return false;
417     }
418     Fieldf="../../../../edu/internet2/middleware/grouper/Field.html#Field">Field that = (Field) other;
419     return new EqualsBuilder()
420       .append( this.name, that.name )
421       .append( this.type, that.type )
422      .isEquals();
423   } // public boolean equals(other)
424 
425   /**
426    * @return name
427    * @since   1.2.0
428    */
429   public String getName() {
430     return this.name;
431   }
432 
433   /**
434    * @return read privilege
435    * @since   1.2.0
436    */
437   public String getReadPrivilege() {
438     return this.readPrivilege;
439   }
440 
441   /**
442    * @return type string
443    * @since   1.2.0
444    */
445   public String getTypeString() {
446     return this.type;
447   }
448 
449   /**
450    * @return uuid
451    * @since   1.2.0
452    */
453   public String getUuid() {
454     return this.uuid;
455   }
456 
457   /**
458    * @return write privilege
459    * @since   1.2.0
460    */
461   public String getWritePrivilege() {
462     return this.writePrivilege;
463   }
464 
465   /**
466    * 
467    * @see java.lang.Object#hashCode()
468    */
469   @Override
470   public int hashCode() {
471     return new HashCodeBuilder()
472       .append( this.name )
473       .append( this.type )
474       .toHashCode();
475   } // public int hashCode()
476 
477   /**
478    * @param name 
479    * @since   1.2.0
480    */
481   public void setName(String name) {
482     this.name = name;
483   
484   }
485 
486   /**
487    * @param readPrivilege 
488    * @since   1.2.0
489    */
490   public void setReadPrivilege(Privilege readPrivilege) {
491     this.readPrivilege = readPrivilege.getName();
492   
493   }
494 
495   /**
496    * @param readPrivilege 
497    * @since   1.2.0
498    */
499   public void setReadPrivilege(String readPrivilege) {
500     this.readPrivilege = readPrivilege;
501   
502   }
503 
504   /**
505    * @param type 
506    * @since   1.2.0
507    */
508   public void setType(FieldType type) {
509     this.type = type.toString();
510   
511   }
512 
513   /**
514    * @param type 
515    * @since   1.2.0
516    */
517   public void setTypeString(String type) {
518     this.type = type;
519   
520   }
521 
522   /**
523    * see if this field is an attribute name
524    * @return true if so
525    * @deprecated
526    */
527   public boolean isAttributeName() {
528     return false;
529   }
530 
531   /**
532    * @param uuid 
533    * @since   1.2.0
534    */
535   public void setUuid(String uuid) {
536     this.uuid = uuid;
537   
538   }
539 
540   /**
541    * @param writePrivilege 
542    * @since   1.2.0
543    */
544   public void setWritePrivilege(Privilege writePrivilege) {
545     this.writePrivilege = writePrivilege.getName();
546   
547   }
548 
549   /**
550    * @param writePrivilege 
551    * @since   1.2.0
552    */
553   public void setWritePrivilege(String writePrivilege) {
554     this.writePrivilege = writePrivilege;
555   
556   }
557 
558   /**
559    * @return string
560    * @since   1.2.0
561    */
562   @Override
563   public String toString() {
564     return new ToStringBuilder(this)
565       .append( "name",           this.getName()           )
566       .append( "readPrivilege",  this.getReadPrivilege()  )
567       .append( "type",           this.getType()           )
568       .append( "uuid",           this.getUuid()           )
569       .append( "writePrivilege", this.getWritePrivilege() )
570       .toString();
571   } // public String toString()
572 
573   /**
574    * @see edu.internet2.middleware.grouper.GrouperAPI#onPostDelete(edu.internet2.middleware.grouper.hibernate.HibernateSession)
575    */
576   @Override
577   public void onPostDelete(HibernateSession hibernateSession) {
578     super.onPostDelete(hibernateSession);
579     
580     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.FIELD, 
581         FieldHooks.METHOD_FIELD_POST_COMMIT_DELETE, HooksFieldBean.class, 
582         this, Field.class);
583 
584     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.FIELD, 
585         FieldHooks.METHOD_FIELD_POST_DELETE, HooksFieldBean.class, 
586         this, Field.class, VetoTypeGrouper.FIELD_POST_DELETE, false, true);
587   }
588 
589   /**
590    * @see edu.internet2.middleware.grouper.GrouperAPI#onPostSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
591    */
592   @Override
593   public void onPostSave(HibernateSession hibernateSession) {
594     super.onPostSave(hibernateSession);
595     
596     // add group sets
597     if (this.type.equals("list") && !Group.getDefaultList().getUuid().equals(this.getUuid())) {
598       Set<Group> groups = GrouperDAOFactory.getFactory().getGroup().findAllByType(this.getGroupType());
599       for (Group group : groups) {
600         if (group.getTypeOfGroup() != null && group.getTypeOfGroup().supportsField(this)) {
601           GroupSet/group/GroupSet.html#GroupSet">GroupSet groupSet = new GroupSet();
602           groupSet.setId(GrouperUuid.getUuid());
603           groupSet.setCreatorId(GrouperSession.staticGrouperSession().getMemberUuid());
604           groupSet.setDepth(0);
605           groupSet.setMemberGroupId(group.getUuid());
606           groupSet.setOwnerGroupId(group.getUuid());
607           groupSet.setParentId(groupSet.getId());
608           groupSet.setFieldId(this.getUuid());
609           GrouperDAOFactory.getFactory().getGroupSet().save(groupSet);
610         }
611       }
612     }
613     
614     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.FIELD, 
615         FieldHooks.METHOD_FIELD_POST_INSERT, HooksFieldBean.class, 
616         this, Field.class, VetoTypeGrouper.FIELD_POST_INSERT, true, false);
617 
618     //do these second so the right object version is set, and dbVersion is ok
619     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.FIELD, 
620         FieldHooks.METHOD_FIELD_POST_COMMIT_INSERT, HooksFieldBean.class, 
621         this, Field.class);
622 
623 
624   }
625 
626   /**
627    * @see edu.internet2.middleware.grouper.GrouperAPI#onPostUpdate(edu.internet2.middleware.grouper.hibernate.HibernateSession)
628    */
629   @Override
630   public void onPostUpdate(HibernateSession hibernateSession) {
631     super.onPostUpdate(hibernateSession);
632 
633     GrouperHooksUtils.schedulePostCommitHooksIfRegistered(GrouperHookType.FIELD, 
634         FieldHooks.METHOD_FIELD_POST_COMMIT_UPDATE, HooksFieldBean.class, 
635         this, Field.class);
636     
637     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.FIELD, 
638         FieldHooks.METHOD_FIELD_POST_UPDATE, HooksFieldBean.class, 
639         this, Field.class, VetoTypeGrouper.FIELD_POST_UPDATE, true, false);
640   }
641 
642   /**
643    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreDelete(edu.internet2.middleware.grouper.hibernate.HibernateSession)
644    */
645   @Override
646   public void onPreDelete(HibernateSession hibernateSession) {
647     super.onPreDelete(hibernateSession);
648     
649     // remove group sets
650     if (this.type.equals("list") && !Group.getDefaultList().getUuid().equals(this.getUuid())) {
651       Set<Group> groups = GrouperDAOFactory.getFactory().getGroup().findAllByType(this.getGroupType());
652       for (Group group : groups) {
653         GrouperDAOFactory.getFactory().getGroupSet().deleteSelfByOwnerGroupAndField(group.getUuid(), this.getUuid());
654       }
655     }
656     
657     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.FIELD, 
658         FieldHooks.METHOD_FIELD_PRE_DELETE, HooksFieldBean.class, 
659         this, Field.class, VetoTypeGrouper.FIELD_PRE_DELETE, false, false);
660   
661     //change log into temp table
662     new ChangeLogEntry(true, ChangeLogTypeBuiltin.GROUP_FIELD_DELETE, 
663         ChangeLogLabels.GROUP_FIELD_DELETE.id.name(), 
664         this.getUuid(), ChangeLogLabels.GROUP_FIELD_DELETE.name.name(), 
665         this.getName(), null, 
666         null,
667         null, 
668         null,
669         ChangeLogLabels.GROUP_FIELD_DELETE.type.name(), this.getTypeString()
670     ).save();
671   }
672 
673   /**
674    * 
675    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreSave(edu.internet2.middleware.grouper.hibernate.HibernateSession)
676    */
677   @Override
678   public void onPreSave(HibernateSession hibernateSession) {
679     super.onPreSave(hibernateSession);
680         
681     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.FIELD, 
682         FieldHooks.METHOD_FIELD_PRE_INSERT, HooksFieldBean.class, 
683         this, Field.class, VetoTypeGrouper.FIELD_PRE_INSERT, false, false);
684   
685     //change log into temp table
686     new ChangeLogEntry(true, ChangeLogTypeBuiltin.GROUP_FIELD_ADD, 
687         ChangeLogLabels.GROUP_FIELD_ADD.id.name(), 
688         this.getUuid(), ChangeLogLabels.GROUP_FIELD_ADD.name.name(), 
689         this.getName(), null, 
690         null,
691         null, 
692         null,
693         ChangeLogLabels.GROUP_FIELD_ADD.type.name(), this.getTypeString()
694     ).save();
695     
696    
697 
698   }
699 
700   /**
701    * @see edu.internet2.middleware.grouper.GrouperAPI#onPreUpdate(edu.internet2.middleware.grouper.hibernate.HibernateSession)
702    */
703   @Override
704   public void onPreUpdate(HibernateSession hibernateSession) {
705     super.onPreUpdate(hibernateSession);
706     
707     GrouperHooksUtils.callHooksIfRegistered(this, GrouperHookType.FIELD, 
708         FieldHooks.METHOD_FIELD_PRE_UPDATE, HooksFieldBean.class, 
709         this, Field.class, VetoTypeGrouper.FIELD_PRE_UPDATE, false, false);
710 
711     //change log into temp table
712     ChangeLogEntry.saveTempUpdates(ChangeLogTypeBuiltin.GROUP_FIELD_UPDATE, 
713         this, this.dbVersion(),
714         GrouperUtil.toList(ChangeLogLabels.GROUP_FIELD_UPDATE.id.name(),this.getUuid(), 
715             ChangeLogLabels.GROUP_FIELD_UPDATE.name.name(), this.getName(),
716             null, 
717             null,
718             null, 
719             null,
720             ChangeLogLabels.GROUP_FIELD_ADD.type.name(), this.getTypeString()),
721         GrouperUtil.toList(FIELD_NAME, FIELD_TYPE, FIELD_READ_PRIVILEGE, FIELD_WRITE_PRIVILEGE),
722         GrouperUtil.toList(ChangeLogLabels.GROUP_FIELD_UPDATE.name.name(),
723             ChangeLogLabels.GROUP_FIELD_UPDATE.type.name(), ChangeLogLabels.GROUP_FIELD_UPDATE.readPrivilege.name(),
724             ChangeLogLabels.GROUP_FIELD_UPDATE.writePrivilege.name()));    
725   }
726 
727   /**
728    * save the state when retrieving from DB
729    * @return the dbVersion
730    */
731   @Override
732   public Field dbVersion() {
733     return (Field)this.dbVersion;
734   }
735 
736   /**
737    * note, these are massaged so that name, extension, etc look like normal fields.
738    * access with fieldValue()
739    * @see edu.internet2.middleware.grouper.GrouperAPI#dbVersionDifferentFields()
740    */
741   @Override
742   public Set<String> dbVersionDifferentFields() {
743     if (this.dbVersion == null) {
744       throw new RuntimeException("State was never stored from db");
745     }
746     //easier to unit test if everything is ordered
747     Set<String> result = GrouperUtil.compareObjectFields(this, this.dbVersion,
748         DB_VERSION_FIELDS, null);
749     return result;
750   }
751 
752   /**
753    * take a snapshot of the data since this is what is in the db
754    */
755   @Override
756   public void dbVersionReset() {
757     //lets get the state from the db so we know what has changed
758     this.dbVersion = GrouperUtil.clone(this, DB_VERSION_FIELDS);
759   }
760 
761   /**
762    * deep clone the fields in this object
763    */
764   @Override
765   public Field clone() {
766     return GrouperUtil.clone(this, CLONE_FIELDS);
767   }
768 
769   /**
770    * store this object to the DB.
771    */
772   public void store() {    
773     GrouperDAOFactory.getFactory().getField().update(this);
774   }
775 
776   /**
777    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlCopyBusinessPropertiesToExisting(java.lang.Object)
778    */
779   public void xmlCopyBusinessPropertiesToExisting(Field existingRecord) {
780     existingRecord.name = this.name;
781     existingRecord.readPrivilege = this.readPrivilege;
782     existingRecord.type = this.type;
783     existingRecord.setUuid(this.getUuid());
784     existingRecord.writePrivilege = this.writePrivilege;
785   }
786 
787   /**
788    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlDifferentBusinessProperties(java.lang.Object)
789    */
790   public boolean xmlDifferentBusinessProperties(Field other) {
791     if (!StringUtils.equals(this.name, other.name)) {
792       return true;
793     }
794     if (!StringUtils.equals(this.readPrivilege, other.readPrivilege)) {
795       return true;
796     }
797     if (!StringUtils.equals(this.type, other.type)) {
798       return true;
799     }
800     if (!StringUtils.equals(this.uuid, other.uuid)) {
801       return true;
802     }
803     if (!StringUtils.equals(this.writePrivilege, other.writePrivilege)) {
804       return true;
805     }
806 
807     return false;
808   }
809 
810   /**
811    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlDifferentUpdateProperties(java.lang.Object)
812    */
813   public boolean xmlDifferentUpdateProperties(Field other) {
814     if (!StringUtils.equals(this.contextId, other.contextId)) {
815       return true;
816     }
817     if (!GrouperUtil.equals(this.getHibernateVersionNumber(), other.getHibernateVersionNumber())) {
818       return true;
819     }
820     return false;
821   }
822 
823   /**
824    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlRetrieveByIdOrKey()
825    */
826   public Field xmlRetrieveByIdOrKey() {
827     return GrouperDAOFactory.getFactory().getField().findByUuidOrName(this.uuid, this.name, false,
828         new QueryOptions().secondLevelCache(false));
829   }
830 
831   /**
832    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlSaveBusinessProperties(java.lang.Object)
833    */
834   public Field/internet2/middleware/grouper/Field.html#Field">Field xmlSaveBusinessProperties(Field existingRecord) {
835     //if its an insert, call the business method
836     if (existingRecord == null) {
837       existingRecord = Field.internal_addField(GrouperSession.staticGrouperSession(), this.name, this.getType(), this.getReadPriv(), this.getWritePriv(), true, false, null, this.uuid);
838     }
839 
840     this.xmlCopyBusinessPropertiesToExisting(existingRecord);
841     //if its an insert or update, then do the rest of the fields
842     existingRecord.store();
843     FieldFinder.clearCache();
844     GroupTypeFinder.clearCache();
845     return existingRecord;
846   }
847 
848   /**
849    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlSaveUpdateProperties()
850    */
851   public void xmlSaveUpdateProperties() {
852     GrouperDAOFactory.getFactory().getField().saveUpdateProperties(this);
853     FieldFinder.clearCache();
854 
855   }
856 
857   /**
858    * convert to xml bean for export
859    * @param grouperVersion
860    * @return xml bean
861    */
862   public XmlExportField xmlToExportField(GrouperVersion grouperVersion) {
863     if (grouperVersion == null) {
864       throw new RuntimeException();
865     }
866 
867     XmlExportFieldXmlExportField.html#XmlExportField">XmlExportField xmlExportField = new XmlExportField();
868     
869     xmlExportField.setContextId(this.getContextId());
870     xmlExportField.setHibernateVersionNumber(this.getHibernateVersionNumber());
871     
872     xmlExportField.setName(this.getName());
873     xmlExportField.setReadPrivilege(this.getReadPrivilege());
874     xmlExportField.setType(this.getTypeString());
875     xmlExportField.setUuid(this.getUuid());
876     xmlExportField.setWritePrivilege(this.getWritePrivilege());
877     return xmlExportField;
878   }
879 
880   /**
881    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlGetId()
882    */
883   public String xmlGetId() {
884     return this.getUuid();
885   }
886 
887   /**
888    * @see edu.internet2.middleware.grouper.xml.export.XmlImportable#xmlSetId(java.lang.String)
889    */
890   public void xmlSetId(String theId) {
891     this.setUuid(theId);
892   }
893   
894   /**
895    * @see edu.internet2.middleware.grouper.xml.export.XmlImportableBase#xmlToString()
896    */
897   public String xmlToString() {
898     StringWriter stringWriter = new StringWriter();
899     
900     stringWriter.write("Field: " + this.getUuid() + ", " + this.getName());
901 
902     return stringWriter.toString();
903     
904   }
905 
906   /**
907    * @param exceptionIfNoGroupType 
908    * @return group type
909    */
910   public GroupType getGroupType(boolean exceptionIfNoGroupType) {
911     if (this.getType() == FieldType.LIST && !this.getUuid().equals(Group.getDefaultList().getUuid())) {
912       return GroupTypeFinder.internal_findGroupTypeByField(this, true);
913     }
914     
915     if (exceptionIfNoGroupType) {
916       throw new RuntimeException("Field " + this.getName() + " does not have a group type.");
917     }
918     
919     return null;
920   }
921   
922   /**
923    * @return group type
924    */
925   public GroupType getGroupType() {
926     return getGroupType(true);
927   }
928   
929   /**
930    * @see java.lang.Comparable#compareTo(java.lang.Object)
931    */
932   public int compareTo(Field that) {
933     if (that==null) {
934       return 1;
935     }
936     String thisName = StringUtils.defaultString(this.getName());
937     String thatName = StringUtils.defaultString(that.getName());
938     return thisName.compareTo(thatName);
939   }
940 
941   /**
942    * add a field if it is not already there
943    * @param s
944    * @param name
945    * @param type
946    * @param read
947    * @param write
948    * @param exceptionIfExists
949    * @param updateIfExists 
950    * @param changedArray is an array of 1 if you want to know if this method changed anything, else null
951    * @param uuid 
952    * @return the field
953    * @throws InsufficientPrivilegeException
954    * @throws SchemaException
955    */
956   public static Field internal_addField(
957     final GrouperSession s, final String name, final FieldType type, final Privilege read, 
958     final Privilege write, final boolean exceptionIfExists, final boolean updateIfExists,
959     final boolean[] changedArray, String uuid) throws  InsufficientPrivilegeException, SchemaException {
960 
961     //these are reserved words:
962     if (Group.INTERNAL_FIELD_ATTRIBUTES.contains(name)) {
963       throw new RuntimeException("You cannot add a field which is a reserved word '" 
964           + name + "', reserved words are : " + GrouperUtil.toStringForLog(Group.INTERNAL_FIELD_ATTRIBUTES));
965     }
966     
967     final String UUID = StringUtils.isBlank(uuid) ? GrouperUuid.getUuid() : uuid;
968     
969     return (Field)HibernateSession.callbackHibernateSession(
970         GrouperTransactionType.READ_WRITE_OR_USE_EXISTING, AuditControl.WILL_AUDIT, new HibernateHandler() {
971 
972       public Object callback(HibernateHandlerBean hibernateHandlerBean)
973           throws GrouperDAOException {
974         
975         try {
976           //note, no need for GrouperSession inverse of control
977           StopWatch sw  = new StopWatch();
978           sw.start();
979           AddFieldToGroupTypeValidator v = AddFieldToGroupTypeValidator.validate(name, !exceptionIfExists);
980           if (v.isInvalid()) {
981             throw new SchemaException( v.getErrorMessage() );
982           }
983           Field field = FieldFinder.find(name, false);
984 
985           if (field != null) {
986             boolean changed = false;
987             if (!type.equals(field.getType())) {
988               //dont want to change types, that could be bad!
989               throw new SchemaException("field '" + name + "' does not have type: " + type + ", it has: " + field.getType());
990             }
991             if (!read.equals(field.getReadPriv())) {
992               if (exceptionIfExists) {
993                 throw new SchemaException("field '" + name + "' does not have read privilege: " + read + ", it has: " + field.getReadPrivilege());
994               }
995               if (updateIfExists) {
996                 changed = true;
997                 field.setReadPrivilege(read);
998               }
999             }
1000             if (!write.equals(field.getWritePriv())) {
1001               if (exceptionIfExists) {
1002                 throw new SchemaException("field '" + name + "' does not have write privilege: " + write + ", it has: " + field.getWritePrivilege());
1003               }
1004               if (updateIfExists) {
1005                 changed = true;
1006                 field.setWritePrivilege(write);
1007               }
1008             }
1009             if (exceptionIfExists) {
1010               throw new SchemaException("field exists: '" + name + "'");
1011             }
1012             //store minor changes to db
1013             if (changed && updateIfExists) {
1014               changed = true;
1015               
1016               String differences = GrouperUtil.dbVersionDescribeDifferences(field.dbVersion(), 
1017                   field, field.dbVersion() != null ? field.dbVersionDifferentFields() : Field.CLONE_FIELDS);
1018               
1019               GrouperDAOFactory.getFactory().getField().createOrUpdate(field);
1020   
1021               if (!hibernateHandlerBean.isCallerWillCreateAudit()) {
1022                 //audit the update
1023                 AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_FIELD_UPDATE, "id", 
1024                     field.getUuid(), "name", field.getName(), "groupTypeId", null, 
1025                     "groupTypeName", null, "type", type.getType());
1026                 
1027                 String description = "Updated group field: " + name + ", id: " + field.getUuid() 
1028                     + ", type: " + type + ".\n" + differences;
1029                 auditEntry.setDescription(description);
1030                 
1031                 auditEntry.saveOrUpdate(true);
1032               }
1033               
1034               if (GrouperUtil.length(changedArray) > 0) {
1035                 changedArray[0] = true;
1036               }
1037             } else {
1038               if (GrouperUtil.length(changedArray) > 0) {
1039                 changedArray[0] = false;
1040               }
1041             }
1042             FieldFinder.internal_updateKnownFields();
1043             return field;
1044           }
1045           if (GrouperUtil.length(changedArray) > 0) {
1046             changedArray[0] = true;
1047           }
1048           try {
1049             field = new Field();
1050             field.setName(name);
1051             field.setReadPrivilege(read);
1052             field.setType(type);
1053             field.setUuid(UUID);
1054             field.setWritePrivilege(write);
1055               
1056             GrouperDAOFactory.getFactory().getField().createOrUpdate(field);
1057             
1058             sw.stop();
1059             EventLog.info(
1060               s, 
1061               M.GROUPTYPE_ADDFIELD + Quote.single(field.getName()) + " ftype=" + Quote.single(type.toString()),
1062               sw
1063             );
1064             FieldFinder.internal_updateKnownFields();
1065           }
1066           catch (GrouperDAOException eDAO) {
1067             String msg = E.GROUPTYPE_FIELDADD + name + ": " + eDAO.getMessage();
1068             LOG.error( msg);
1069             throw new SchemaException(msg, eDAO);
1070           }
1071           
1072           
1073           //only audit if actually changed the type
1074           AuditEntryit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.GROUP_FIELD_ADD, "id", 
1075               field.getUuid(), "name", field.getName(), "groupTypeId", null, "groupTypeName", null, "type", type.getType());
1076           auditEntry.setDescription("Added group field: " + name + ", id: " + field.getUuid() + ", type: " + type);
1077           auditEntry.saveOrUpdate(true);
1078           
1079           return field;
1080         } catch (GrouperDAOException eDAO) {
1081           String msg = E.GROUPTYPE_FIELDADD + name + ": " + eDAO.getMessage();
1082           LOG.error( msg);
1083           throw new SchemaException(msg, eDAO);
1084         }
1085       }
1086       
1087     });
1088     
1089     
1090     
1091     
1092   }
1093 } // public class Field extends GrouperAPI implements Serializable
1094