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 2004-2006 The University Of Bristol
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.xml;
34  import java.io.File;
35  import java.io.IOException;
36  import java.text.DateFormat;
37  import java.text.ParseException;
38  import java.text.SimpleDateFormat;
39  import java.util.ArrayList;
40  import java.util.Collection;
41  import java.util.Date;
42  import java.util.HashMap;
43  import java.util.Iterator;
44  import java.util.LinkedHashSet;
45  import java.util.List;
46  import java.util.Map;
47  import java.util.Properties;
48  import java.util.Set;
49  import java.util.Vector;
50  
51  import org.apache.commons.lang.StringUtils;
52  import org.apache.commons.logging.Log;
53  import org.w3c.dom.CharacterData;
54  import org.w3c.dom.Document;
55  import org.w3c.dom.Element;
56  import org.w3c.dom.Node;
57  import org.w3c.dom.NodeList;
58  import org.w3c.dom.Text;
59  
60  import edu.internet2.middleware.grouper.Field;
61  import edu.internet2.middleware.grouper.FieldFinder;
62  import edu.internet2.middleware.grouper.FieldType;
63  import edu.internet2.middleware.grouper.Group;
64  import edu.internet2.middleware.grouper.GroupFinder;
65  import edu.internet2.middleware.grouper.GroupType;
66  import edu.internet2.middleware.grouper.GroupTypeFinder;
67  import edu.internet2.middleware.grouper.GrouperSession;
68  import edu.internet2.middleware.grouper.Member;
69  import edu.internet2.middleware.grouper.MemberFinder;
70  import edu.internet2.middleware.grouper.Membership;
71  import edu.internet2.middleware.grouper.Stem;
72  import edu.internet2.middleware.grouper.StemFinder;
73  import edu.internet2.middleware.grouper.SubjectFinder;
74  import edu.internet2.middleware.grouper.attr.AttributeDefName;
75  import edu.internet2.middleware.grouper.attr.assign.AttributeAssign;
76  import edu.internet2.middleware.grouper.audit.AuditEntry;
77  import edu.internet2.middleware.grouper.audit.AuditTypeBuiltin;
78  import edu.internet2.middleware.grouper.audit.GrouperEngineBuiltin;
79  import edu.internet2.middleware.grouper.cfg.GrouperConfig;
80  import edu.internet2.middleware.grouper.exception.AttributeNotFoundException;
81  import edu.internet2.middleware.grouper.exception.GrantPrivilegeException;
82  import edu.internet2.middleware.grouper.exception.GroupAddException;
83  import edu.internet2.middleware.grouper.exception.GroupModifyException;
84  import edu.internet2.middleware.grouper.exception.GroupNotFoundException;
85  import edu.internet2.middleware.grouper.exception.GrouperException;
86  import edu.internet2.middleware.grouper.exception.GrouperSessionException;
87  import edu.internet2.middleware.grouper.exception.InsufficientPrivilegeException;
88  import edu.internet2.middleware.grouper.exception.MemberAddException;
89  import edu.internet2.middleware.grouper.exception.MemberDeleteException;
90  import edu.internet2.middleware.grouper.exception.RevokePrivilegeException;
91  import edu.internet2.middleware.grouper.exception.SchemaException;
92  import edu.internet2.middleware.grouper.exception.SessionException;
93  import edu.internet2.middleware.grouper.exception.StemAddException;
94  import edu.internet2.middleware.grouper.exception.StemModifyException;
95  import edu.internet2.middleware.grouper.exception.StemNotFoundException;
96  import edu.internet2.middleware.grouper.hibernate.AuditControl;
97  import edu.internet2.middleware.grouper.hibernate.GrouperContext;
98  import edu.internet2.middleware.grouper.hibernate.GrouperTransactionType;
99  import edu.internet2.middleware.grouper.hibernate.HibernateHandler;
100 import edu.internet2.middleware.grouper.hibernate.HibernateHandlerBean;
101 import edu.internet2.middleware.grouper.hibernate.HibernateSession;
102 import edu.internet2.middleware.grouper.internal.dao.GrouperDAOException;
103 import edu.internet2.middleware.grouper.internal.util.Quote;
104 import edu.internet2.middleware.grouper.internal.util.U;
105 import edu.internet2.middleware.grouper.misc.CompositeType;
106 import edu.internet2.middleware.grouper.misc.E;
107 import edu.internet2.middleware.grouper.misc.GrouperDAOFactory;
108 import edu.internet2.middleware.grouper.misc.GrouperSessionHandler;
109 import edu.internet2.middleware.grouper.misc.GrouperStartup;
110 import edu.internet2.middleware.grouper.privs.Privilege;
111 import edu.internet2.middleware.grouper.subj.LazySubject;
112 import edu.internet2.middleware.grouper.util.GrouperUtil;
113 import edu.internet2.middleware.grouper.validator.NotNullOrEmptyValidator;
114 import edu.internet2.middleware.grouper.xml.userAudit.XmlUserAuditImport;
115 import edu.internet2.middleware.subject.Subject;
116 import edu.internet2.middleware.subject.SubjectNotFoundException;
117 import edu.internet2.middleware.subject.SubjectNotUniqueException;
118 
119 /**
120  * Utility class for importing data in XML import into the Groups Registry.
121  * <p>
122  * This class reads an XML file representing all or part of a Groups Registry
123  * and updates-or-creates the equivalent {@link Stem}s, {@link Group}s and
124  * {@link Membership}s.  This class can be used to load data exported by 
125  * {@link XmlExporter}.
126  * <p/>
127  * <p><b>The API for this class will change in future Grouper releases.</b></p>
128  * @author  Gary Brown.
129  * @author  blair christensen.
130  * @version $Id: XmlImporter.java,v 1.24.2.2 2009-12-18 21:03:44 tzeller Exp $
131  * @since   1.0
132  */
133 public class XmlImporter {
134 
135   // PRIVATE CLASS CONSTANTS //  
136   private static final String CF            = "import.properties";
137   private static final Log    LOG           = GrouperUtil.getLog(XmlImporter.class);
138   private static final String MODE_ADD      = "add";
139   private static final String MODE_IGNORE   = "ignore";
140   private static final String MODE_REPLACE  = "replace";
141   private static final String SPECIAL_C     = "c"; // i'm assuming this refers to a composite mship
142   private static final String SPECIAL_E     = "e"; // i'm assuming this refers to an effective mship
143 
144 
145   // PRIVATE INSTANCE VARIABLES //
146   private List            accessPrivLists = new ArrayList();
147   private Document        doc;
148   private Map             importedGroups  = new HashMap();
149   private String          importRoot; // Anchor import here
150   private List            membershipLists = new ArrayList();
151   private List            namingPrivLists = new ArrayList();
152   private Properties      options         = new Properties();
153   private GrouperSession  s;
154   private boolean         updateOnly      = false;
155   private boolean         ignoreInternal  = false;
156   
157   
158   // CONSTRUCTORS //
159   
160   /**
161    * Import the Groups Registry from XML.
162    * <p>
163    * The import process is configured through the following properties.
164    * </p>
165    * <table width="90%" border="1">
166    * <tr>
167    * <td>Key</td>
168    * <td>Values</td>
169    * <td>Default</td>
170    * <td>Description</td>
171    * </tr>
172    * <tr>
173    * <td>import.metadata.group-types</td>
174    * <td>true/false</td>
175    * <td>true</td>
176    * <td>If true create custom group types when importing.</td>
177    * </tr>
178    * <tr>
179    * <td>import.metadata.group-type-attributes</td>
180    * <td>true/false</td>
181    * <td>true</td>
182    * <td>If true create custom fields when importing.</td>
183    * </tr>
184    * <tr>
185    * <td>import.data.ignore-internal-attributes-and-uuids=false</td>
186    * <td>true/false</td>
187    * <td>false</td>
188    * <td>If true, do not attempt to set internal attributes or Group/Stem uuids</td>
189    * </tr>
190    * <tr>
191    * <td>import.data.apply-new-group-types</td>
192    * <td>true/false</td>
193    * <td>true</td>
194    * <td>If true custom group types are applied to pre-existing groups when importing.</td>
195    * </tr>
196    * <tr>
197    * <td>import.data.update-attributes</td>
198    * <td>true/false</td>
199    * <td>true</td>
200    * <td>If true overwrite attributes on pre-existing groups when importing.</td>
201    * </tr>
202    * <tr>
203    * <td>import.data.fail-on-unresolvable-subject</td>
204    * <td>true/false</td>
205    * <td>false</td>
206    * <td>If true, and the import file references a subject which cannot be resolved 
207    * abort the import, otherwise, log the problem and continue.
208    * </td>
209    * </tr>
210    * <tr>
211    * <td>import.data.lists</td>
212    * <td>ignore/replace/add</td>
213    * <td>replace</td>
214    * <td>Determines whether membership lists are ignored, replaced or appended to pre-existing memberships when importing.</td>
215    * </tr>
216    * <tr>
217    * <td>import.data.privileges</td>
218    * <td>ignore/replace/add</td>
219    * <td>add</td>
220    * <td>Determines whether privileges are ignored, replaced or appended to pre-existing privileges when importing.</td>
221    * </tr>
222    * </table>
223    * @param   s           Perform import within this session.
224    * @param   userOptions User-specified configuration parameters.
225    * @since   1.1.0
226    */
227   public XmlImporter(GrouperSession s, Properties userOptions) 
228   {
229     try {
230       this.options  = XmlUtils.internal_getSystemProperties(LOG, CF);
231     }
232     catch (IOException eIO) {
233       throw new GrouperException(eIO.getMessage(), eIO);
234     }
235     this.options.putAll(userOptions); 
236     this.s = s;
237   } // public XmlImporter(s, userOptions)
238 
239 
240   // MAIN //
241 
242   /**
243    * Process an Xml file as the 'root' user.
244    * <p/>
245    * @param   args    args[0] = name of Xml file to process
246    * @since   1.1.0
247    */
248   public static void main(final String[] args) {
249     
250     //set this and leave it...
251     GrouperContext.createNewDefaultContext(GrouperEngineBuiltin.IMPORT, false, true);
252 
253     if (XmlArgs.internal_wantsHelp(args)) {
254       System.out.println( _getUsage() );
255       //System.exit(0);
256       return;
257     }
258     final Properties rc;
259     try {
260       rc = XmlArgs.internal_getXmlImportArgs(args);
261     }
262     catch (Exception e) {
263       e.printStackTrace();
264       System.err.println();
265       System.err.println( _getUsage() );
266       //System.exit(1);
267       return;
268     }
269     
270   //make sure right db
271     if(!"true".equals(rc.getProperty(XmlArgs.RC_NOPROMPT))) {
272       GrouperUtil.promptUserAboutDbChanges("import data from xml", true);
273     }
274     GrouperStartup.runFromMain = true;
275     GrouperStartup.startup();
276     final XmlImporterml/XmlImporter.html#XmlImporter">XmlImporter[] importer = new XmlImporter[1];
277     
278     GrouperSession.internal_callbackRootGrouperSession(new GrouperSessionHandler() {
279 
280       @Override
281       public Object callback(GrouperSession grouperSession) throws GrouperSessionException {
282         HibernateSession.callbackHibernateSession(
283             GrouperTransactionType.NONE, AuditControl.WILL_AUDIT,
284             new HibernateHandler() {
285 
286               public Object callback(HibernateHandlerBean hibernateHandlerBean)
287                   throws GrouperDAOException {
288                 try { 
289                   
290                   importer[0]  = new XmlImporter(
291                     GrouperSession.start(
292                       SubjectFinder.findByIdentifier( rc.getProperty(XmlArgs.RC_SUBJ), true ), false
293                     ),
294                     XmlUtils.internal_getUserProperties(LOG, rc.getProperty(XmlArgs.RC_UPROPS))
295                   );
296                   _handleArgs(importer[0], rc);
297                   
298                   GrouperSession staticSession = GrouperSession.staticGrouperSession(false);
299                   if(staticSession==null) {
300                     staticSession = GrouperSession.start(SubjectFinder.findByIdentifier( rc.getProperty(XmlArgs.RC_SUBJ), true));  
301                   }
302                   AuditEntryaudit/AuditEntry.html#AuditEntry">AuditEntry auditEntry = new AuditEntry(AuditTypeBuiltin.XML_IMPORT, "fileName", 
303                       rc.getProperty(XmlArgs.RC_IFILE), "subjectId", rc.getProperty(XmlArgs.RC_SUBJ));
304                   auditEntry.setDescription("Imported xml: " + GrouperUtil.toStringForLog(args));
305                   auditEntry.saveOrUpdate(true);
306                   staticSession.stop();
307                   
308                 } catch (Exception e) {
309                   LOG.fatal("unable to import from xml: " + e.getMessage(), e);
310                   System.out.println("unable to import from xml: " + e.getMessage());
311                   e.printStackTrace();
312                   //System.exit(1);
313                 } finally {
314                   if (importer[0] != null) {
315                     try {
316                       importer[0].s.stop();
317                     }
318                     catch (SessionException eS) {
319                       LOG.error(eS.getMessage());
320                     }
321                   }
322                 }
323                 return null;
324               }
325             });
326           
327         LOG.debug("Finished import of [" + rc.getProperty(XmlArgs.RC_IFILE) + "]");
328         return null;
329       }
330     });
331     
332     System.exit(0);
333     return;
334   } // public static void main(args)
335 
336 
337   // PUBLIC INSTANCE METHODS //
338 
339   /**
340    * Populate Groups Registry.
341    * <pre class="eg">
342    * try {
343    *   importer.load( XmlReader.getDocumentFromString(s) );
344    * }
345    * catch (GrouperException eG) {
346    *   // error importing
347    * }
348    * </pre>
349    * @param   doc   Import this <tt>Document</tt>.
350    * @throws  GrouperException
351    * @throws  IllegalArgumentException if <tt>doc</tt> is null
352    * @since   1.1.0
353    */
354   public void load(final Document doc)
355     throws  GrouperException,
356             IllegalArgumentException
357   {
358     LOG.info("starting load at root stem");
359     XmlImporter.this._load( StemFinder.findRootStem(this.s), doc );
360     LOG.info("finished load");
361   } // public void load(xml)
362 
363   /**
364    * Populate Groups Registry using the specified <tt>Stem</tt> as the root of
365    * the registry.
366    * <pre class="eg">
367    * try {
368    *   importer.load( ns, XmlReader.getDocumentFromString(s) );
369    * }
370    * catch (GrouperException eG) {
371    *   // error importing
372    * }
373    * </pre>
374    * @param   ns    Import using this <tt>Stem</tt> as the <i>root stem</i>.
375    * @param   doc   Import this <tt>Document</tt>.
376    * @throws  GrouperException
377    * @throws  IllegalArgumentException if <tt>doc</tt> is null
378    * @since   1.1.0
379    */
380   public void load(Stem ns, Document doc)
381     throws  GrouperException,
382             IllegalArgumentException
383   {
384     LOG.info("starting load at " + Quote.single(ns.getName()));
385     this._load(ns, doc);
386     LOG.info("finished load");
387   } // public void load(ns, doc)
388 
389   /**
390    * Update memberships and privileges but do not create missing stems or groups.
391    * <pre class="eg">
392    * try {
393    *   importer.update( XmlReader.getDocumentFromString(s) );
394    * }
395    * catch (GrouperException eG) {
396    *   // error updating
397    * }
398    * </pre>
399    * @param   doc   Import this <tt>Document</tt>.
400    * @throws  GrouperException
401    * @throws  IllegalArgumentException if <tt>doc</tt> is null
402    * @since   1.1.0
403    */
404   public void update(Document doc)
405     throws  GrouperException,
406             IllegalArgumentException
407   {
408     LOG.info("starting update");
409     this._setUpdateOnly(true);
410     this._load( StemFinder.findRootStem(this.s), doc );
411     LOG.info("finished update");
412   } // public void update(doc)
413 
414   //@since 1.4.0
415   public void setIgnoreInternal(boolean ignoreInternal) {
416 	  this.ignoreInternal=ignoreInternal;
417   }
418   
419   // PROTECTED INSTANCE METHODS //
420 
421   // @since   1.2.0
422   public Properties internal_getOptions() {
423     return (Properties) options.clone();
424   } // protected Properties internal_getOptions()
425 
426 
427   // PRIVATE CLASS METHODS //
428 
429   // @since   1.1.0
430   private Subject _findSubject(String id, String idfr, String type) 
431     throws  SubjectNotFoundException,
432             SubjectNotUniqueException
433   {
434     NotNullOrEmptyValidator v = NotNullOrEmptyValidator.validate(id);
435     if (v.isInvalid()) {
436       if (type.equals("group")) {
437         if (this._isRelativeImport(idfr)) {
438           v = NotNullOrEmptyValidator.validate(this.importRoot);
439           if (v.isValid()) {
440             idfr = U.constructName( this.importRoot, idfr.substring(1) );
441           }
442           else {
443             idfr = idfr.substring(1);
444           }
445         }
446         else {
447           LOG.warn("not absolutizing idfr: " + Quote.single(idfr));
448         }
449       }
450       return this._getSubjectByIdentifier(idfr, type);
451     } 
452     return this._getSubjectById(id, type);
453   } // private Subject _findSubject(id, idfr, type)
454 
455   // Assumes tag only occurs once and contains only text / CDATA.
456   // If tag does not exist 'nullable' determines if an Exception is thrown.
457   // @since   1.1.0
458   private static String _getText(Element element) 
459     throws  GrouperException
460   {
461     element.normalize();
462     NodeList nl = element.getChildNodes();
463     if (nl.getLength() != 1) {
464       throw (new GrouperException("Cannot process " + element.getTagName() + " tag"));
465     }
466     Node n = nl.item(0);
467     if (
468       n.getNodeType() != Node.TEXT_NODE
469       && n.getNodeType() != Node.CDATA_SECTION_NODE
470     ) 
471     {
472       throw (new GrouperException("Cannot process " + element.getTagName() + " tag"));
473     }
474     return ((CharacterData) n).getData().trim();
475   } // private static String _getText(element)
476 
477   // @since   1.1.0
478   private static String _getUsage() {
479     return  "Usage:"                                                                + GrouperConfig.NL
480             + "args: -h,            Prints this message"                            + GrouperConfig.NL
481             + "args: subjectIdentifier "             + GrouperConfig.NL
482             + "      [-userAuditFilename <fileName>] [-userAuditOnly]"              + GrouperConfig.NL 
483             + "      [(-id <id> | -name <name> | -list)]"                           + GrouperConfig.NL
484             + "      [-ignoreInternal] [-noprompt] filename [properties]"           + GrouperConfig.NL
485             + "e.g.  gsh -xmlimport -userAuditFilename f:/temp/prodAudit.xml GrouperSystem f:/temp/prod.xml"                 + GrouperConfig.NL
486             +                                                                         GrouperConfig.NL
487             + "  subjectIdentifier, Identifies a Subject 'who' will create a"       + GrouperConfig.NL
488             + "                     GrouperSession"                                 + GrouperConfig.NL
489             + "  -userAuditFilename,The file name where user audits should go"      + GrouperConfig.NL
490             + "  -id,               The Uuid of a Stem, into which, data will be"   + GrouperConfig.NL
491             + "                     imported"                                       + GrouperConfig.NL
492             + "  -name,             The name of a Stem, into which, data will be"   + GrouperConfig.NL
493             + "                     imported.  If no -id / -name is specified, "    + GrouperConfig.NL
494             + "                     use=ROOT stem."                                 + GrouperConfig.NL
495             + "  -list,             File contains a flat list of Stems or Groups"   + GrouperConfig.NL
496             + "                     which may be updated. Missing Stems and Groups" + GrouperConfig.NL
497             + "                     are not created"                                + GrouperConfig.NL
498             
499             + "  -ignoreInternal,   Do not attempt to import internal attributes"   + GrouperConfig.NL
500             + "                     including Group/Stem uuids. Overrides property:"+ GrouperConfig.NL
501             + "                     import.data.ignore-internal-attributes-and-uuids"+GrouperConfig.NL
502             
503             + "  -noprompt,         Do not prompt user to confirm the database that"+ GrouperConfig.NL
504             + "                     will be updated"                                + GrouperConfig.NL
505             
506             + "  filename,          The file to import"                             + GrouperConfig.NL
507             + "  properties,        The name of an optional Java properties file. " + GrouperConfig.NL
508             + "                     Values specified in this properties file will " + GrouperConfig.NL
509             + "                     override the default import behavior."          + GrouperConfig.NL
510             ;
511   } // private static String _getUsage()
512 
513   /**
514    * @since   1.1.0
515    * @param importer
516    * @param rc
517    * @throws GrouperException
518    */
519   public static void _handleArgs(final XmlImporter importer, final Properties rc) 
520     throws  GrouperException {
521     try {
522       
523       String userAuditFilename = rc.getProperty("userAuditFilename");
524       if (!StringUtils.isBlank(userAuditFilename)) {
525         
526         new XmlUserAuditImport().readUserAudits(new File(userAuditFilename));
527         
528       }
529 
530       String userAuditOnly = rc.getProperty("userAuditOnly");
531       if (StringUtils.equalsIgnoreCase("true", userAuditOnly)) {
532         //we are done
533         return;
534       }
535 
536       importer.ignoreInternal = Boolean.parseBoolean( rc.getProperty(XmlArgs.RC_IGNORE) );
537       GrouperSession.callbackGrouperSession(importer.s, new GrouperSessionHandler() {
538   
539         public Object callback(GrouperSession grouperSession)
540             throws GrouperSessionException {
541           try {
542             Document doc = XmlReader.getDocumentFromFile( rc.getProperty(XmlArgs.RC_IFILE) );
543             if (Boolean.parseBoolean( rc.getProperty(XmlArgs.RC_UPDATELIST) )) {
544               importer.update(doc);
545             } 
546             else {
547               if (rc.getProperty(XmlArgs.RC_UUID) == null && rc.getProperty(XmlArgs.RC_NAME) == null) {
548                 importer.load(doc);
549               } 
550               else {
551                 Stem    ns    = null;
552                 String  uuid  = rc.getProperty(XmlArgs.RC_UUID);
553                 String  name  = rc.getProperty(XmlArgs.RC_NAME);
554                 if      (uuid != null) {
555                   try {
556                     ns = StemFinder.findByUuid(importer.s, uuid, true);
557                   } catch (StemNotFoundException e) {
558                     throw new IllegalArgumentException(E.NO_STEM_UUID + Quote.single(uuid));
559                   }
560                 } 
561                 else if (name != null) {
562                   try {
563                     ns = StemFinder.findByName(importer.s, name, true);
564                   } catch (StemNotFoundException e) {
565                     throw new IllegalArgumentException(E.NO_STEM_NAME + Quote.single(name));
566                   }
567                 }
568                 if (ns == null) {
569                   throw new IllegalArgumentException(E.NO_STEM);
570                 }
571                 importer.load(ns, doc);
572               }
573             } 
574           } catch (GrouperException grouperException) {
575             throw new GrouperSessionException(grouperException);
576           }
577           return null;
578         }
579         
580       });
581     } catch (GrouperSessionException grouperSessionException) {
582       if (grouperSessionException.getCause() instanceof GrouperException) {
583         throw (GrouperException)grouperSessionException.getCause();
584       }
585       throw grouperSessionException;
586     }
587   } // private static void _handleArgs(importer, rc);
588 
589 
590   // PRIVATE INSTANCE METHODS //
591 
592   // @since   1.1.0
593   private void _accumulateLists(Element e, String group) 
594   {
595     Collection  lists = this._getImmediateElements(e, "list");
596     Iterator    it    = lists.iterator();
597     Element     list;
598     Map map;
599     while (it.hasNext()) {
600       list  = (Element) it.next();
601       map   = new HashMap();
602       map.put("group", group);
603       map.put("list", list);
604       membershipLists.add(map);
605     }
606   } // private void _accumulateLists(e, group)
607 
608   // @since   1.1.0
609   private void _accumulatePrivs(Element e, String stem, String type)
610     throws  GrouperException
611   {
612     Collection  privileges  = this._getImmediateElements(e, "privileges");
613     Iterator    it          = privileges.iterator();
614     Element     privilege;
615     String      priv;
616     Map         map;
617     boolean     isGroup     = "access".equals(type);
618     while (it.hasNext()) {
619       privilege = (Element) it.next();
620       priv      = privilege.getAttribute("type");
621       map       = new HashMap();
622 
623       map.put(type, priv);
624       map.put("privileges", privilege);
625       if (isGroup) {
626         map.put("group", stem);
627         accessPrivLists.add(map);
628       } else {
629         map.put("stem", stem);
630         this.namingPrivLists.add(map);
631       }
632     }
633   } // private void _accumulatePrivs(e, stem, type)
634 
635   // @since   1.0
636   private String _getAbsoluteName(String name, String stem) {
637 	 name=name.replaceAll("^\\*",".:");
638     if (name != null && (name.startsWith("."))) {
639       if (name.startsWith("." + Stem.ROOT_INT)) {
640         name = stem + name.substring(1);
641       } 
642       else {
643         while (name.startsWith(".." + Stem.ROOT_INT)) {
644           name = name.substring(3);
645           stem = stem.substring(0, stem.lastIndexOf(Stem.ROOT_INT));
646         }
647         name = U.constructName(stem, name);
648       }
649     }
650     NotNullOrEmptyValidator v = NotNullOrEmptyValidator.validate(importRoot);
651     if (
652       v.isValid() && this.importedGroups.containsKey(importRoot + Stem.ROOT_INT + name)
653     ) 
654     {
655       return importRoot + Stem.ROOT_INT + name;
656     }
657     return name;
658   } // private String _getAbsoluteName(name, stem)
659 
660   // @since   1.1.0
661   private String _getDataListImportMode() {
662     return this.options.getProperty("import.data.lists", MODE_IGNORE);
663   } // private String _getDataListImportMode()
664 
665   // @since   1.1.0
666   private String _getDataPrivilegesImportMode() {
667     return this.options.getProperty("import.data.privileges", MODE_IGNORE);
668   } // private String _getDataPrivilegesImportMode()
669 
670   // Returns immediate child element with given name
671   // TODO 2009-12-18 tz : includes all child elements
672   // @since   1.0
673   private Element _getImmediateElement(Element element, String name)
674   { 
675     NodeList nl = element.getElementsByTagName(name);
676     if (nl.getLength() < 1) {
677       return null;
678     } 
679     if (nl.getLength() > 1) {
680       throw new IllegalArgumentException(E.ELEMENT_NOT_UNIQUE + name);
681     }
682     return (Element) nl.item(0);
683   } // private Element _getImmediateElement(element, name)
684   
685   // Returns immediate child elements with given name
686   // @since   1.0
687   private Collection _getImmediateElements(Element element, String name)
688   { 
689     Collection<Element> elements = new Vector<Element>();
690     if (element != null) {
691       Element   child;
692       NodeList  nl    = element.getElementsByTagName(name);
693       for (int i = 0; i < nl.getLength(); i++) {
694         child = (Element) nl.item(i);
695         if (child.getParentNode().equals(element)) {
696           elements.add(child);
697         }
698       }
699     }
700     return elements;
701   } // private Collection _getImmediateElements(element, name)
702 
703   // @since   1.1.0
704   private Properties _getImportOptionsFromXml() 
705     throws  GrouperException
706   {
707     LOG.debug("Attempting to find importOptions in XML");
708     Element rootE = doc.getDocumentElement();
709     Element importOptionsE = this._getImmediateElement(rootE, "importOptions");
710     if (importOptionsE == null) {
711       LOG.debug("No importOptions tag in XML");
712       return null;
713     }
714     LOG.debug("Found importOptions tag in XML - loading options");
715     Collection options = this._getImmediateElements(importOptionsE, "options");
716     Element optionE;
717     Properties props = new Properties();
718     Iterator it = options.iterator();
719     while (it.hasNext()) {
720       optionE = (Element) it.next();
721       props.put(optionE.getAttribute("key"), XmlImporter._getText(optionE));
722       LOG.debug("Loading " + optionE.getAttribute("key") + "="
723           + XmlImporter._getText(optionE));
724     }
725     LOG.debug("Finished loading options from XML");
726 
727     return props;
728   } // private Properties _getImportedOptionsFromXml()
729 
730   // @since   1.1.0
731   private Collection _getInternalAttributes(Element e) {
732     Set   attrs = new LinkedHashSet();
733     List  l     = new ArrayList( this._getImmediateElements(e, "internalAttributes" ) );
734     if (l.size() == 1) {
735       attrs.addAll( this._getImmediateElements( (Element) l.get(0), "internalAttribute" ) );
736     }
737     return attrs;
738   } // private Collection _getInternalAttributes(e)
739 
740   // @since   1.0
741   private Subject _getSubjectById(String id, String type) 
742     throws  SubjectNotFoundException,
743             SubjectNotUniqueException
744   {
745     NotNullOrEmptyValidator v = NotNullOrEmptyValidator.validate(type);
746     if (v.isInvalid()) {
747       return SubjectFinder.findById(id, true);
748     }
749     return SubjectFinder.findById(id, true);
750   } // private Subject _getSubjectById(id, type)
751 
752   // @since   1.0
753   private Subject _getSubjectByIdentifier(String identifier, String type)
754     throws  SubjectNotFoundException,
755             SubjectNotUniqueException
756   {
757     NotNullOrEmptyValidator v = NotNullOrEmptyValidator.validate(type);
758     if (v.isInvalid()) {
759       return SubjectFinder.findByIdentifier(identifier, true);
760     }
761     return SubjectFinder.findByIdentifier(identifier, true);
762   } // private Subject _getSubjectByIdentifier(identifier, type)
763 
764   // @since   1.1.0
765   private boolean _isApplyNewGroupTypesEnabled() {
766     return XmlUtils.internal_getBooleanOption(this.options, "import.data.apply-new-group-types");
767   } // private boolean _isApplyNewGroupTypesEnabled()
768   
769   // @since   1.1.0
770   private boolean _isMetadataGroupTypeImportEnabled() {
771     return XmlUtils.internal_getBooleanOption(this.options, "import.metadata.group-types");
772   } // private boolean _isMetadataGroupTypeImportEnabled()
773 
774   // @since   1.1.0
775   private boolean _isMetadataGroupTypeAttributeImportEnabled() {
776     return XmlUtils.internal_getBooleanOption(this.options, "import.metadata.group-type-attributes");
777   } // private boolean _isMetadataGroupTypeAttributeImportEnabled()
778 
779   // @since   1.1.0
780   private boolean _isRelativeImport(String idfr) {
781     return (
782           idfr.startsWith(  XmlUtils.SPECIAL_STAR )
783       &&  !idfr.endsWith(   XmlUtils.SPECIAL_STAR )
784     );
785   } // private boolean _isRelativeImport(idfr)
786  
787   // @since   1.1.0
788   private boolean _isSubjectElementImmediate(Element el) {
789     return Boolean.valueOf( el.getAttribute("immediate") ).booleanValue();
790   }
791 
792   //@since   1.4.0
793   private boolean _isIgnoreInternalAttributes() {
794     return (this.ignoreInternal || 
795     	XmlUtils.internal_getBooleanOption(this.options, "import.data.ignore-internal-attributes-and-uuids"));
796   } // private boolean _isIgnoreInternalAttributes()
797   
798   
799   // @since   1.1.0
800   private boolean _isUpdatingAttributes() {
801     return XmlUtils.internal_getBooleanOption(this.options, "import.data.update-attributes");
802   } // private boolean _isUpdatingAttributes()
803 
804 //@since   1.3.1
805   private boolean _isFailOnUnresolvableSubjectEnabled() {
806     return XmlUtils.internal_getBooleanOption(this.options, "import.data.fail-on-unresolvable-subject");
807   } // private boolean _isFailOnUnresolvableSubjectEnabled()
808 
809   // @since   1.1.0
810   private void _load(final Stem ns, final Document doc) 
811     throws  GrouperException,
812             IllegalArgumentException
813   {
814     
815     try {
816       GrouperSession.callbackGrouperSession(this.s, new GrouperSessionHandler() {
817   
818         public Object callback(GrouperSession grouperSession)
819             throws GrouperSessionException {
820           try {
821             XmlImporter.this._setDocument(doc);
822             try {
823               XmlImporter.this.importRoot = ns.getName();
824               if (ns.isRootStem()) {
825                 XmlImporter.this.importRoot = GrouperConfig.EMPTY_STRING;
826               }
827               XmlImporter.this._processProperties();
828               Element root = XmlImporter.this._getDocument().getDocumentElement();
829 
830               XmlImporter.this._processMetadata(XmlImporter.this._getImmediateElement(root, "metadata"));
831               XmlImporter.this._process( XmlImporter.this._getImmediateElement(root, "data"), XmlImporter.this.importRoot );
832               XmlImporter.this._processMembershipLists();
833               XmlImporter.this._processNamingPrivLists();
834               XmlImporter.this._processAccessPrivLists();
835             }
836             catch (AttributeNotFoundException eANF)     {
837               throw new GrouperException(eANF.getMessage(), eANF);
838             }
839             catch (GrantPrivilegeException eGP)         {
840               throw new GrouperException(eGP.getMessage(), eGP);
841             }
842             catch (GroupAddException eGA)               {
843               throw new GrouperException(eGA.getMessage(), eGA);
844             }
845             catch (GrouperDAOException eDAO)             {
846               throw new GrouperException( eDAO.getMessage(), eDAO );
847             }
848             catch (GroupModifyException eGM)            {
849               throw new GrouperException(eGM.getMessage(), eGM);
850             }
851             catch (GroupNotFoundException eGNF)         {
852               throw new GrouperException(eGNF.getMessage(), eGNF);
853             }
854             catch (InsufficientPrivilegeException eIP)  {
855               throw new GrouperException(eIP.getMessage(), eIP);
856             }
857             catch (MemberAddException eMA)              {
858               throw new GrouperException(eMA.getMessage(), eMA);
859             }
860             catch (MemberDeleteException eMD)           {
861               throw new GrouperException(eMD.getMessage(), eMD);
862             }
863             catch (RevokePrivilegeException eRP)        {
864               throw new GrouperException(eRP.getMessage(), eRP);
865             }
866             catch (SchemaException eS)                  {
867               throw new GrouperException(eS.getMessage(), eS);
868             }
869             catch (StemAddException eNSA)               {
870               throw new GrouperException(eNSA.getMessage(), eNSA);
871             }
872             catch (StemModifyException eNSM)            {
873               throw new GrouperException(eNSM.getMessage(), eNSM);
874             }
875             catch (StemNotFoundException eNSNF)         {
876               throw new GrouperException(eNSNF.getMessage(), eNSNF);
877             }
878             catch (SubjectNotFoundException eSNF)       {
879               throw new GrouperException(eSNF.getMessage(), eSNF);
880             }
881             catch (SubjectNotUniqueException eSNU)      {
882               throw new GrouperException( eSNU.getMessage(), eSNU );
883             }
884           } catch (GrouperException grouperException) {
885             throw new GrouperSessionException(grouperException);
886           }
887           return null;
888         }
889         
890       });
891     } catch (GrouperSessionException grouperSessionException) {
892       if (grouperSessionException.getCause() instanceof GrouperException) {
893         throw (GrouperException)grouperSessionException.getCause();
894       }
895       throw grouperSessionException;
896     }
897 
898     
899   } // private void _load(ns, doc)
900 
901   // @since   1.0
902   private boolean _optionTrue(String key) {
903     NotNullOrEmptyValidator v = NotNullOrEmptyValidator.validate(key);
904     if (v.isInvalid()) {
905       options.setProperty(key, "false");
906       return false;
907     }
908     return "true".equals( options.getProperty(key) );
909   } // private boolean _optionTrue(key)
910 
911   // @since   1.1.0
912   private Date _parseTime(String s) 
913     throws  ParseException
914   {
915     Date d = null;
916     try {
917       // First check to see if we are using the new export date format (ms since epoch)
918       d = new Date( Long.parseLong(s) );
919     }
920     catch (NumberFormatException eNF) {
921       // Guess not.  Try to parse the old format and hope for the best.
922       DateFormat df = new SimpleDateFormat("E MMM dd HH:mm:ss Z yyyy");
923       d = df.parse(s);
924     }
925     return d;
926   } // private Date _parseTime(s)
927 
928   // For each stem list and process any child stems. List and process any child groups.
929   // @since   1.1.0
930   private void _process(Element e, String stem) 
931     throws  AttributeNotFoundException,
932             GroupAddException,
933             GrouperDAOException,
934             GrouperException,
935             GroupModifyException,
936             GroupNotFoundException,
937             InsufficientPrivilegeException,
938             SchemaException,
939             StemAddException,
940             StemModifyException,
941             StemNotFoundException
942   {
943     if (e != null) {
944       this._processPaths(e, stem);
945       this._processGroups(e, stem);
946     }
947   } // private void _process(e, stem)
948 
949   // @since   1.1.0
950   private void _processAccessPrivLists() 
951     throws  GrantPrivilegeException,
952             GroupNotFoundException,
953             InsufficientPrivilegeException,
954             RevokePrivilegeException,
955             SchemaException,
956             SubjectNotFoundException,
957             SubjectNotUniqueException
958   {
959     if (this.accessPrivLists != null) {
960       Iterator it = this.accessPrivLists.iterator();
961       while (it.hasNext()) {
962         this._processAccessPrivList( (Map) it.next() );
963       }
964     }
965     this.accessPrivLists = null;
966   } // private void _processAccessPrivLists()
967 
968   // @since   1.1.0
969   private void _processAccessPrivList(Map map) 
970     throws  GrantPrivilegeException,
971             GroupNotFoundException,
972             InsufficientPrivilegeException,
973             RevokePrivilegeException,
974             SchemaException,
975             SubjectNotFoundException,
976             SubjectNotUniqueException
977   {
978     String  groupName = (String) map.get("group");
979     Element privs     = (Element) map.get("privileges");
980     if (this._getDataPrivilegesImportMode().equals(MODE_IGNORE)) {
981       return; // Ignore privileges
982     }
983     try {
984       Group     g = GroupFinder.findByName(s, groupName, true);
985       Privilege p = Privilege.getInstance( privs.getAttribute("type") );
986       if (this._getDataPrivilegesImportMode().equals(MODE_REPLACE)) {
987         g.revokePriv(p);
988       }
989       Iterator it       = this._getImmediateElements(privs, "subject").iterator();
990       while (it.hasNext()) {
991         this._processAccessPrivListGrantPriv( g, p, (Element) it.next() );
992       }
993     }
994     catch (GroupNotFoundException eGNF) {
995       if (!this._getUpdateOnly()) {
996         throw eGNF; // if updating we can ignore, if loading we cannot
997       }
998     }
999   } // private void _processAccessPrivList(map)
1000 
1001   // @since   1.1.0
1002   private void _processAccessPrivListGrantPriv(Group g, Privilege p, Element el)
1003     throws  GrantPrivilegeException,
1004             InsufficientPrivilegeException,
1005             SchemaException,
1006             SubjectNotFoundException,
1007             SubjectNotUniqueException
1008   {
1009     if ( this._isSubjectElementImmediate(el) ) {
1010       Subject subj = null;
1011       String attributeId = el.getAttribute("id");
1012       String attributeIdentifier = el.getAttribute("identifier");
1013       String attributeType = el.getAttribute("type");
1014       try {
1015         subj=this._findSubject(attributeId, attributeIdentifier, attributeType);
1016       }catch(Exception e) {
1017         String errorMessage = "Could not grant " + p.getName() + " to " + g.getName() + " for subject id=" + attributeId;
1018     	  if(_isFailOnUnresolvableSubjectEnabled()) {
1019     	    
1020     	    if (e instanceof SubjectNotFoundException) {
1021     	      throw (SubjectNotFoundException)e;
1022     	    }
1023           if (e instanceof SubjectNotUniqueException) {
1024             throw (SubjectNotUniqueException)e;
1025           }
1026           if (e instanceof RuntimeException) {
1027             throw (RuntimeException)e;
1028           }
1029     		  throw new RuntimeException(errorMessage, e);
1030     	  }
1031         LOG.error(errorMessage,e);
1032     		  return;
1033       }
1034       if ( !XmlUtils.internal_hasImmediatePrivilege( subj, g, p.getName() ) ) {
1035         g.grantPriv(subj, p, false);
1036       }
1037     }
1038   } // private void _processAccessPrivListGrantPriv(g, p, el)
1039 
1040   // @since   1.1.0
1041   private void _processAttributes(Element e, String group) 
1042     throws  AttributeNotFoundException,
1043             GroupModifyException,
1044             GroupNotFoundException,
1045             InsufficientPrivilegeException,
1046             SchemaException
1047   {
1048     Element   elTypes = this._getImmediateElement(e, "groupTypes");
1049     if (elTypes == null) {
1050       return;
1051     }
1052     Group     g       = GroupFinder.findByName(s, group, true);
1053     Iterator  it      = this._getImmediateElements(elTypes, "groupType").iterator();
1054     while (it.hasNext()) {
1055       this._processAttributesHandleType( g, (Element) it.next() );
1056     }
1057   } // private void _processAttributes(e, stem) 
1058 
1059   // @since   1.1.10
1060   private void _processAttributesHandleType(Group g, Element e)
1061     throws  AttributeNotFoundException,
1062             GroupModifyException,
1063             InsufficientPrivilegeException
1064   {
1065     String name = e.getAttribute("name");
1066     if (!name.equals("base")) {
1067       try {
1068         GroupType gt = GroupTypeFinder.find(name, true);
1069         if (!g.hasType(gt)) {
1070           if (this._optionTrue("import.data.apply-new-group-types")) {
1071             g.addType(gt);
1072           }
1073         }
1074         this._processAttributesHandleAttributes(g, gt, e);
1075       }
1076       catch (SchemaException eS) {
1077         LOG.error(eS.getMessage());
1078       }
1079     }
1080   } // privae void _processAttributesHandleType(g, e)
1081 
1082   // @since   1.1.0
1083   private void _processAttributesHandleAttributes(Group g, GroupType gt, Element e) 
1084     throws  AttributeNotFoundException,
1085             GroupModifyException,
1086             InsufficientPrivilegeException,
1087             SchemaException
1088   {
1089     Element   elAttr;
1090     String    name, orig, val;
1091     Iterator  it      = this._getImmediateElements(e, "attribute").iterator();
1092     while (it.hasNext()) {
1093       elAttr  = (Element) it.next();
1094       name    = elAttr.getAttribute("name");
1095       AttributeDefName f = GrouperDAOFactory.getFactory().getAttributeDefName().findLegacyAttributeByName(name, true);
1096       
1097       AttributeAssign groupTypeAssignment = g.internal_getGroupTypeAssignments().get(gt.getName());
1098       if (groupTypeAssignment == null) {
1099         throw new AttributeNotFoundException("Group " + g.getName() + " is not assigned the group type: " + gt.getName());
1100       }
1101       
1102       try {
1103         groupTypeAssignment.getAttributeDelegate().assertCanUpdateAttributeDefName(f);
1104       } catch (InsufficientPrivilegeException ex) {
1105         LOG.debug("cannot write (" + name + ") on (" + g.getName() + ")");
1106         continue;
1107       } catch (AttributeNotFoundException ex) {
1108         LOG.debug("cannot write (" + name + ") on (" + g.getName() + ")");
1109         continue;
1110       }
1111       
1112       orig                          = g.getAttributeValue(name, false, false); 
1113       try {
1114     	  val                           = ( (Text) elAttr.getFirstChild() ).getData();
1115       }catch(NullPointerException npe) {
1116     	  val=null;
1117       }
1118       NotNullOrEmptyValidator vOrig = NotNullOrEmptyValidator.validate(orig);
1119       NotNullOrEmptyValidator vVal  = NotNullOrEmptyValidator.validate(val);
1120       if ( vVal.isValid() && !val.equals(orig) && ( vOrig.isInvalid() || this._isUpdatingAttributes() ) ) {
1121         g.setAttribute(name, val);
1122       } 
1123     }
1124   } // private void _processAttributesHandleAttributes()
1125 
1126   // @since   1.1.0
1127   private void _processComposite(Element el, Group g)
1128     throws  GrouperException,
1129             InsufficientPrivilegeException,
1130             MemberAddException
1131   {
1132     if (g.hasComposite()) { 
1133       LOG.warn(g.getName() + " already has composite - skipping");
1134       return;
1135     }
1136     el.normalize(); // i'm going to assume this is import.  so be it.
1137     Element[]     elements    = new Element[3];
1138     NodeList      nl          = el.getChildNodes();
1139     int           elCount     = -1;
1140     Node          node;
1141     for (int i = 0; i < nl.getLength(); i++) {
1142       node = nl.item(i);
1143       if (node instanceof Element) {
1144         elCount++;
1145         if (elCount > 2) {
1146           throw new IllegalStateException(
1147             "Too many tags in <composite>. Expect <groupRef><compositeType><groupRef>"
1148           );
1149         }
1150         elements[elCount] = (Element) node;
1151       }
1152     }
1153     try {
1154       g.addCompositeMember(
1155         this._processCompositeType( elements[1] ),
1156         this._processGroupRef( elements[0], g.getParentStem().getName() ),
1157         this._processGroupRef( elements[2], g.getParentStem().getName() )
1158       );
1159     } 
1160     catch (GroupNotFoundException eGNF) {
1161       LOG.error("error processing composite for " + Quote.single(g.getName()) + ": " + eGNF.getMessage());
1162       return;
1163     }
1164   } // private void _processComposite(composite, group)
1165 
1166   // @since   1.1.0
1167   private CompositeType _processCompositeType(Element typeE) 
1168     throws  GrouperException
1169   {
1170     String tag = typeE.getTagName();
1171     if (!tag.equals("compositeType")) {
1172       throw new IllegalStateException("Expected tag: <compositeType> but found <" + tag + ">");
1173     }
1174     String name = XmlImporter._getText(typeE);
1175     CompositeType ctype = CompositeType.valueOfIgnoreCase(name);
1176     if (ctype == null) {
1177       throw new IllegalStateException("could not resolve composite type: " + Quote.single(name));
1178     }
1179     return ctype;
1180   }  // private CompositeType _processCompositeType(typeE)
1181 
1182   // @since   1.1.0
1183   private void _processGroup(Element e, String stem) 
1184     throws  AttributeNotFoundException,
1185             GroupAddException,
1186             GrouperDAOException,
1187             GrouperException,
1188             GroupModifyException,
1189             GroupNotFoundException,
1190             InsufficientPrivilegeException,
1191             SchemaException,
1192             StemNotFoundException
1193   {
1194     String newGroup = U.constructName( stem, e.getAttribute(GrouperConfig.ATTRIBUTE_EXTENSION) );
1195     try {
1196       this._processGroupUpdate(e, newGroup);  // Try and update
1197     } 
1198     catch (GroupNotFoundException eGNF) {
1199       this._processGroupCreate(e, stem);      // Otherwise create
1200     }
1201     this._processAttributes(e, newGroup);
1202     this._accumulateLists(e, newGroup);
1203     this._accumulatePrivs(e, newGroup, "access");
1204   } // private void _processGroup(e, stem)
1205 
1206   // @since   1.1.0
1207   private void _processGroupCreate(Element e, String stem) 
1208     throws  GroupAddException,
1209             GrouperDAOException,
1210             GroupModifyException,
1211             InsufficientPrivilegeException,
1212             StemNotFoundException
1213   {
1214     if (this._getUpdateOnly()) {
1215       return; // do not create groups when we are only updating
1216     }
1217     Stem  parent  = StemFinder.findByName(this.s, stem, true);
1218     String id=null;
1219     if(!_isIgnoreInternalAttributes()) {
1220     	id=e.getAttribute("id");
1221     }
1222     Group child   = parent.internal_addChildGroup(
1223       e.getAttribute(GrouperConfig.ATTRIBUTE_EXTENSION),
1224       e.getAttribute(GrouperConfig.ATTRIBUTE_DISPLAY_EXTENSION),
1225       id
1226     );
1227     String desc = this._getDescription(e);
1228     NotNullOrEmptyValidator v     = NotNullOrEmptyValidator.validate(desc);
1229     if (v.isValid()) {
1230       child.setDescription(desc);
1231       child.store();
1232     }
1233     if(!_isIgnoreInternalAttributes()) {
1234     	this._setInternalAttributes(child, e);
1235     }
1236     this.importedGroups.put( child.getName(), SPECIAL_C );
1237   } // private void _processGroupCreate(e, stem)
1238 
1239   // @since   1.1.0
1240   private Group _processGroupRef(Element groupE, String stem) 
1241     throws  GroupNotFoundException
1242   {
1243     String tagName = groupE.getTagName();
1244     if (!"groupRef".equals(tagName)) {
1245       throw new IllegalStateException("Expected tag: <groupRef> but found <" + tagName + ">");
1246     }
1247     String                  name  = groupE.getAttribute(GrouperConfig.ATTRIBUTE_NAME);
1248     NotNullOrEmptyValidator v     = NotNullOrEmptyValidator.validate(name);
1249     if (v.isInvalid()) {
1250       throw new IllegalStateException("Expected 'name' atribute for <groupRef>");
1251     }
1252     return GroupFinder.findByName( s, this._getAbsoluteName(name, stem), true );
1253   } // private Group _processGroupRef(groupE, stem)
1254 
1255   // @since   1.1.0
1256   private void _processGroups(Element e, String stem) 
1257     throws  AttributeNotFoundException,
1258             GroupAddException,
1259             GrouperDAOException,
1260             GrouperException,
1261             GroupModifyException,
1262             GroupNotFoundException,
1263             InsufficientPrivilegeException,
1264             SchemaException,
1265             StemAddException,
1266             StemModifyException,
1267             StemNotFoundException
1268   {
1269     Collection  groups  = this._getImmediateElements(e, "group");
1270     Iterator    it      = groups.iterator();
1271     while (it.hasNext()) {
1272       this._processGroup( (Element) it.next(), stem );
1273     }
1274   } // private void _processGroups(e, stem)
1275 
1276   // @since   1.1.0
1277   private void _processGroupUpdate(Element e, String newGroup) 
1278     throws  GroupModifyException,
1279             GroupNotFoundException,
1280             InsufficientPrivilegeException
1281   {
1282     // We need to keep this outside the conditional so that a
1283     // GroupNotFoundException can be thrown if the stem does not exist.  That
1284     // will trigger the creation of the group.
1285     Group g = GroupFinder.findByName(this.s, newGroup, true);
1286     if (this._isUpdatingAttributes()) {
1287       String                  dExtn = e.getAttribute(GrouperConfig.ATTRIBUTE_DISPLAY_EXTENSION);
1288       NotNullOrEmptyValidator v     = NotNullOrEmptyValidator.validate(dExtn);
1289       if ( v.isValid() && !dExtn.equals( g.getDisplayExtension() ) ) {
1290         g.setDisplayExtension(dExtn);
1291         g.store();
1292       }
1293       String desc = this._getDescription(e);      
1294       v           = NotNullOrEmptyValidator.validate(desc);
1295       if ( v.isValid() && !desc.equals( g.getDescription() ) ) {
1296         g.setDescription(desc);
1297         g.store();
1298       }
1299     }
1300     this.importedGroups.put( g.getName(), SPECIAL_E );
1301   } // private void _processGroupUpdate(e, newGroup)
1302 
1303   // @since   1.1.0
1304   private void _processMembershipLists() 
1305     throws  GrouperException,
1306             GroupModifyException,
1307             GroupNotFoundException,
1308             InsufficientPrivilegeException,
1309             MemberAddException,
1310             MemberDeleteException,
1311             SchemaException,
1312             SubjectNotFoundException,
1313             SubjectNotUniqueException
1314   {
1315     if (this.membershipLists != null) {
1316       Iterator it = this.membershipLists.iterator();
1317       while (it.hasNext()) {
1318         this._processMembershipList( (Map) it.next() );
1319       }
1320     }
1321     this.membershipLists = null;
1322   } // private void _processMembershipLists()
1323 
1324   // @since   1.1.0
1325   private void _processMembershipList(Map map) 
1326     throws  GrouperException,
1327             GroupModifyException,
1328             GroupNotFoundException,
1329             InsufficientPrivilegeException,
1330             MemberAddException,
1331             MemberDeleteException,
1332             SchemaException,
1333             SubjectNotFoundException,
1334             SubjectNotUniqueException
1335   {
1336     Element list      = (Element) map.get("list");
1337     String  groupName = (String) map.get("group");
1338     if (this._getDataListImportMode().equals(MODE_IGNORE)) {
1339       return; // Ignore lists
1340     }
1341     try {
1342       Group   g = GroupFinder.findByName(s, groupName, true);
1343       Field   f = FieldFinder.find( list.getAttribute("field"), true );
1344       if (!f.getType().equals(FieldType.LIST)) {
1345         throw new SchemaException("field is not a list: " + f.getName());
1346       }
1347       
1348       if (!f.getUuid().equals(Group.getDefaultList().getUuid())) {
1349         this._processMembershipListAddGroupType(g, f.getGroupType(true));
1350       }
1351       if (!g.canWriteField(f)) {
1352         return;  // We can't write to the field so don't even bother trying
1353       }
1354       if (!this._processMembershipListHandleImportMode(g, f, list)) {
1355         return; // Stop processing as we've done everything already
1356       }
1357       Iterator it = this._getImmediateElements(list, "subject").iterator();
1358       while (it.hasNext()) {
1359         this._processMembershipListAddMember( g, f, (Element) it.next() );
1360       }
1361     }
1362     catch (GroupNotFoundException eGNF) {
1363       if (!this._getUpdateOnly()) {
1364         throw eGNF; // if updating we can ignore, if loading we cannot
1365       }
1366     }
1367   } // private void _processMembershipList()
1368 
1369   // @since   1.1.0
1370   private void _processMembershipListAddGroupType(Group g, GroupType gt) 
1371     throws  GroupModifyException,
1372             InsufficientPrivilegeException,
1373             SchemaException
1374   {
1375     if ( !g.hasType(gt) && this._isApplyNewGroupTypesEnabled() ) {
1376       g.addType(gt); 
1377     }
1378   } // private void _processMembershipListAddGroupType(g, gt)
1379 
1380   // @since   1.1.0
1381   private void _processMembershipListAddMember(Group g, Field f, Element el) 
1382     throws  InsufficientPrivilegeException,
1383             MemberAddException,
1384             SchemaException,
1385             SubjectNotFoundException,
1386             SubjectNotUniqueException
1387   {
1388     if ( this._isSubjectElementImmediate(el) ) {
1389       Subject subj = null;
1390       try {
1391 	      subj=this._findSubject( 
1392         el.getAttribute("id"), el.getAttribute("identifier"), el.getAttribute("type") 
1393       );
1394       }catch(Exception e) {
1395         String errorMessage = "Could not add member to field " + f.getName() + " of " + g.getName() + " for subject id=" + el.getAttribute("id");
1396     	  if(_isFailOnUnresolvableSubjectEnabled()) {
1397           
1398           if (e instanceof SubjectNotFoundException) {
1399             throw (SubjectNotFoundException)e;
1400           }
1401           if (e instanceof SubjectNotUniqueException) {
1402             throw (SubjectNotUniqueException)e;
1403           }
1404           if (e instanceof RuntimeException) {
1405             throw (RuntimeException)e;
1406           }
1407           throw new RuntimeException(errorMessage, e);
1408         }
1409         LOG.error(errorMessage,e);
1410     		  return;
1411     	  }
1412      
1413       if ( !g.hasImmediateMember(subj, f) ) {
1414         g.addMember(subj, f);
1415       }
1416     }
1417   } // private void _processMembershipListAddMember(g, f, el)
1418 
1419   // @since   1.1.0
1420   private boolean _processMembershipListHandleImportMode(Group g, Field f, Element list) 
1421     throws  GrouperException,
1422             InsufficientPrivilegeException,
1423             MemberAddException,
1424             MemberDeleteException,
1425             SchemaException,
1426             SubjectNotFoundException
1427   {
1428     // TODO 20070321 this needs more refactoring
1429     //      So this handles elminating current members if in replace mode and
1430     //      then adding a composite mship if that's our thing?  Why are they
1431     //      combined?
1432     boolean rv            = true; // If true continue processing 
1433     boolean hasComposite  = g.hasComposite();
1434     boolean hasMembers    = false;
1435     if (!hasComposite && g.getImmediateMembers().size() > 0) {
1436       hasMembers = true;
1437     }
1438     Element compE = this._getImmediateElement(list, "composite");
1439 
1440     if (this._getDataListImportMode().equals(MODE_REPLACE)) {
1441       if (hasComposite) {
1442         g.deleteCompositeMember();
1443       } 
1444       else {
1445         Iterator it = g.getImmediateMembers(f).iterator();
1446         while (it.hasNext()) {
1447           Memberref="../../../../../edu/internet2/middleware/grouper/Member.html#Member">Member m = (Member) it.next();
1448           Subject subj = new LazySubject(m);
1449           g.deleteMember(subj, f);
1450           //g.deleteMember( ( (Member) it.next() ).getSubject() );
1451         }
1452       }
1453     }
1454     if (compE != null && ( !this._getDataListImportMode().equals(MODE_ADD) || hasMembers) ) {
1455       this._processComposite(compE, g);
1456       rv = false; // Omit remaining processing
1457     }
1458     if (compE != null && hasMembers) {
1459       LOG.warn("Cannot add composite membership to group that already has members: " + Quote.single(g.getName()));
1460       rv = false; // Omit remaining processing
1461     }
1462     return rv;
1463   } // private boolean _processMembershipListHandleImportMode(g, f, list)
1464 
1465   // @since   1.1.0
1466   private void _processMetadata(Element el) 
1467     throws  InsufficientPrivilegeException,
1468             SchemaException
1469   {
1470     if ( el == null || !this._isMetadataGroupTypeImportEnabled() ) {
1471       return;
1472     }
1473     Iterator it = this._getImmediateElements(
1474       this._getImmediateElement(el, "groupTypesMetaData"), "groupTypeDef"
1475     ).iterator();
1476     while (it.hasNext()) {
1477       this._processMetadataGroupType( (Element) it.next() );  
1478     }
1479   } // private void _processMetadata(e)
1480 
1481   // @since   1.1.0
1482   private void _processMetadataField(GroupType gt, boolean isNew, Element el) 
1483     throws  InsufficientPrivilegeException,
1484             SchemaException
1485   {
1486     if (isNew || this._isMetadataGroupTypeAttributeImportEnabled()) {
1487       // if a new group type or we have enabled group type attr importing // continue
1488       String    fName = el.getAttribute("name");
1489 
1490       //these were moved from attributes to other things
1491       if (StringUtils.equals(gt.getName(), "base") &&
1492           (
1493           StringUtils.equals("name", fName)
1494           || StringUtils.equals("description", fName)
1495           || StringUtils.equals("extension", fName)
1496           || StringUtils.equals("displayExtension", fName)
1497           || StringUtils.equals("displayName", fName)
1498           )) {
1499         return;
1500       }
1501       
1502       String    fType = el.getAttribute("type");
1503       
1504       if (fType.equals(FieldType.LIST.toString())) {
1505         Privilege read  = Privilege.getInstance( el.getAttribute("readPriv")  );
1506         Privilege write = Privilege.getInstance( el.getAttribute("writePriv") );
1507         try {
1508           FieldFinder.find(fName, true); // already exists
1509         } 
1510         catch (SchemaException eS) {
1511           gt.addList(s, fName, read, write);
1512         }
1513       } else if (fType.equals("attribute")) {
1514         gt.addAttribute(s, fName, false);
1515       } else {
1516         // previous code was ignoring this case..
1517       }
1518     }
1519   } // private void _processMetadataField(gt, isNew, el)
1520 
1521   // @since   1.1.0
1522   private void _processMetadataGroupType(Element el) 
1523     throws  InsufficientPrivilegeException,
1524             SchemaException
1525   {
1526     boolean   isNew   = false;
1527     String    gtName  = el.getAttribute("name");
1528     GroupType gt      = null;
1529     try {
1530       gt = GroupTypeFinder.find(gtName, true);
1531     } 
1532     catch (SchemaException ex) {
1533       gt    = GroupType.createType(s, gtName);
1534       isNew = true;
1535     }
1536     Iterator it = this._getImmediateElements(el, "field").iterator();
1537     while (it.hasNext()) {
1538       this._processMetadataField( gt, isNew, (Element) it.next() );
1539     }
1540   } // private void _processMetadataGroupType(el)
1541 
1542   // @since   1.1.0
1543   private void _processNamingPrivLists() 
1544     throws  GrantPrivilegeException,
1545             GroupModifyException,
1546             InsufficientPrivilegeException,
1547             RevokePrivilegeException,
1548             SchemaException,
1549             StemNotFoundException,
1550             SubjectNotFoundException,
1551             SubjectNotUniqueException
1552   {
1553     if (this.namingPrivLists != null) {
1554       Iterator it = this.namingPrivLists.iterator();
1555       while (it.hasNext()) {
1556         this._processNamingPrivList( (Map) it.next() );
1557       }
1558     }
1559     this.namingPrivLists = null;
1560   } // private void _processNamingPrivLists()
1561 
1562   // @since   1.1.0
1563   private void _processNamingPrivList(Map map) 
1564     throws  GrantPrivilegeException,
1565             InsufficientPrivilegeException,
1566             RevokePrivilegeException,
1567             SchemaException,
1568             StemNotFoundException,
1569             SubjectNotFoundException,
1570             SubjectNotUniqueException
1571   {
1572     String    stemName  = (String) map.get("stem");
1573     Element   privs     = (Element) map.get("privileges");
1574     if (this._getDataPrivilegesImportMode().equals(MODE_IGNORE)) {
1575       return; // Ignore privileges
1576     }
1577     try {
1578       Stem      ns        = StemFinder.findByName(s, stemName, true);
1579       Privilege p         = Privilege.getInstance( privs.getAttribute("type") );
1580       if (this._getDataPrivilegesImportMode().equals(MODE_REPLACE)) {
1581         ns.revokePriv(p);
1582       }
1583       Iterator  it        = this._getImmediateElements(privs, "subject").iterator();
1584       while (it.hasNext()) {
1585         this._processNamingPrivListGrantPriv( ns, p, (Element) it.next() );
1586       }
1587     }
1588     catch (StemNotFoundException eNSNF) {
1589       if (!this._getUpdateOnly()) {
1590         throw eNSNF; // if updating we can ignore, if loading we cannot
1591       }
1592     }
1593   } // private void _processNamingPrivList(map)
1594 
1595   // @since   1.1.0
1596   private void _processNamingPrivListGrantPriv(Stem ns, Privilege p, Element el)
1597     throws  GrantPrivilegeException,
1598             InsufficientPrivilegeException,
1599             SchemaException,
1600             SubjectNotFoundException,
1601             SubjectNotUniqueException
1602   {
1603     if ( this._isSubjectElementImmediate(el) ) {
1604       Subject subj = null;
1605       try {
1606 	      subj=this._findSubject( 
1607         el.getAttribute("id"), el.getAttribute("identifier"), el.getAttribute("type") 
1608       );
1609       }catch(Exception e) {
1610         String errorMessage = "Could not grant " + p.getName() + " to " + ns.getName() + " for subject id=" + el.getAttribute("id");
1611     	  if(_isFailOnUnresolvableSubjectEnabled()) {
1612           
1613           if (e instanceof SubjectNotFoundException) {
1614             throw (SubjectNotFoundException)e;
1615           }
1616           if (e instanceof SubjectNotUniqueException) {
1617             throw (SubjectNotUniqueException)e;
1618           }
1619           if (e instanceof RuntimeException) {
1620             throw (RuntimeException)e;
1621           }
1622           throw new RuntimeException(errorMessage, e);
1623         }
1624         LOG.error(errorMessage,e);
1625     		  return;
1626 
1627       }
1628       
1629       if ( !XmlUtils.internal_hasImmediatePrivilege( subj, ns, p.getName() ) ) {
1630         ns.grantPriv(subj, p, false);
1631       }
1632     }
1633   } // private void _processNamingPrivListGrantPriv(ns, p, el)
1634 
1635   // @since   1.1.0
1636   private void _processPath(Element e, String stem) 
1637     throws  AttributeNotFoundException,
1638             GroupAddException,
1639             GrouperDAOException,
1640             GrouperException,
1641             GroupModifyException,
1642             GroupNotFoundException,
1643             InsufficientPrivilegeException,
1644             SchemaException,
1645             StemAddException,
1646             StemModifyException,
1647             StemNotFoundException
1648   {
1649     String newStem = U.constructName( stem, e.getAttribute(GrouperConfig.ATTRIBUTE_EXTENSION) );
1650     try {
1651       this._processPathUpdate(e, newStem);  // Try and update
1652     } 
1653     catch (StemNotFoundException eNSNF) {
1654       this._processPathCreate(e, stem);     // Otherwise create
1655     }
1656     this._accumulatePrivs(e, newStem, "naming");
1657     this._process(e, newStem); // And now handle the child
1658   } // private void _processPath(e, stem)
1659 
1660   // @since   1.1.0
1661   private void _processPathCreate(Element e, String stem) 
1662     throws  GrouperDAOException,
1663             InsufficientPrivilegeException,
1664             StemAddException,
1665             StemModifyException,
1666             StemNotFoundException
1667   {
1668     if (this._getUpdateOnly()) {
1669       return; // do not create stems when we are only updating
1670     }
1671     Stem parent = null;
1672     if (stem.equals(GrouperConfig.EMPTY_STRING)) {
1673       parent = StemFinder.findRootStem(this.s);
1674     }
1675     else {
1676       parent = StemFinder.findByName(this.s, stem, true);
1677     }
1678     String id = null;
1679     if(!_isIgnoreInternalAttributes()) {
1680     	id=e.getAttribute("id");
1681     }
1682     Stem child = parent.internal_addChildStem(
1683       e.getAttribute(GrouperConfig.ATTRIBUTE_EXTENSION),
1684       e.getAttribute(GrouperConfig.ATTRIBUTE_DISPLAY_EXTENSION),
1685       id
1686     );
1687     String                  desc  = this._getDescription(e);
1688     NotNullOrEmptyValidator v     = NotNullOrEmptyValidator.validate(desc);
1689     if (v.isValid()) {
1690       child.setDescription(desc);
1691       child.store();
1692     }
1693     if(!_isIgnoreInternalAttributes()) {
1694     	this._setInternalAttributes(child, e);
1695     }
1696   } // private void _processPathCreate(e, stem)
1697 
1698   // @since   1.1.0
1699   private void _processPathUpdate(Element e, String newStem) 
1700     throws  InsufficientPrivilegeException,
1701             StemModifyException,
1702             StemNotFoundException
1703   {
1704     // We need to keep this outside the conditional so that a
1705     // StemNotFoundException can be thrown if the stem does not exist.  That
1706     // will trigger the creation of the stem.
1707     Stem ns = StemFinder.findByName(this.s, newStem, true);
1708     if (this._isUpdatingAttributes()) {
1709       String                  dExtn = e.getAttribute(GrouperConfig.ATTRIBUTE_DISPLAY_EXTENSION);
1710       NotNullOrEmptyValidator v     = NotNullOrEmptyValidator.validate(dExtn);
1711       if ( v.isValid() && !dExtn.equals( ns.getDisplayExtension() ) ) {
1712         ns.setDisplayExtension(dExtn);
1713         ns.store();
1714       }
1715       String desc = this._getDescription(e);
1716       v           = NotNullOrEmptyValidator.validate(desc);
1717       if ( v.isValid() && !desc.equals( ns.getDescription() ) ) {
1718         ns.setDescription(desc);
1719         ns.store();
1720       }
1721     }
1722   } // private void _processPathUpdate(e, newStem)
1723 
1724   // @since   1.1.0
1725   private void _processPaths(Element e, String stem) 
1726     throws  AttributeNotFoundException,
1727             GroupAddException,
1728             GrouperDAOException,
1729             GrouperException,
1730             GroupModifyException,
1731             GroupNotFoundException,
1732             InsufficientPrivilegeException,
1733             SchemaException,
1734             StemAddException,
1735             StemModifyException,
1736             StemNotFoundException
1737   {
1738     Iterator it = this._getImmediateElements(e, "stem").iterator();
1739     while (it.hasNext()) {
1740       this._processPath( (Element) it.next(), stem );
1741     }
1742   } // private void _processPaths(e, stem)
1743 
1744   // @since   1.1.0
1745   private void _processProperties() 
1746     throws  GrouperException
1747   {
1748     Properties xmlOptions = this._getImportOptionsFromXml();
1749     if (xmlOptions == null && this.options.isEmpty()) {
1750       throw new IllegalStateException("No options have been set");
1751     }
1752     if (xmlOptions == null) {
1753       return;
1754     }
1755     xmlOptions.putAll(this.options);  // add current to xml
1756     this.options = xmlOptions;        // replace current with merged options
1757   } // private void _processProperties()
1758 
1759   // @since   1.2.0
1760   private void _setCreateSubject(Group g, Element e) {
1761     Element elSubj = this._getImmediateElement(e, "subject");
1762     g.setCreatorUuid(
1763       MemberFinder.internal_findOrCreateBySubject(
1764         elSubj.getAttribute("id"), elSubj.getAttribute("source"), elSubj.getAttribute("type")
1765       ).getUuid()
1766     );
1767   } // private void setCreateSubject(g, e)
1768   
1769   // @since   1.2.0
1770   private void _setCreateSubject(Stem ns, Element e) {
1771     Element elSubj = this._getImmediateElement(e, "subject");
1772     ns.setCreatorUuid(
1773       MemberFinder.internal_findOrCreateBySubject(
1774         elSubj.getAttribute("id"), elSubj.getAttribute("source"), elSubj.getAttribute("type")
1775       ).getUuid()
1776     );
1777   } // private void setCreateSubject(ns, e)
1778 
1779   // @since   1.2.0
1780   private boolean _setCreateTime(Group g, Element e) {
1781     String msg = "error setting createTime: ";
1782     try {
1783       g.setCreateTimeLong( this._parseTime( XmlImporter._getText(e) ).getTime() );
1784       return true;
1785     } catch (GrouperException eG) {
1786        msg += eG.getMessage();
1787     } catch (ParseException eP) {
1788       msg += eP.getMessage();
1789     }
1790     LOG.error(msg);
1791     return false;
1792   } // private boolean _setCreateTime(g, e)
1793 
1794   // @since   1.2.0
1795   private boolean _setCreateTime(Stem ns, Element e) {
1796     String msg = "error setting createTime: ";
1797     try {
1798       ns.setCreateTimeLong( this._parseTime( XmlImporter._getText(e) ).getTime() );
1799       return true;
1800     } catch (GrouperException eG) {
1801        msg += eG.getMessage();
1802     } catch (ParseException eP) {
1803       msg += eP.getMessage();
1804     }
1805     LOG.error(msg);
1806     return false;
1807   } // private boolean _setCreateTime(ns, e)
1808 
1809   // @since   1.2.0
1810   private void _setInternalAttributes(Group g, Element e) 
1811     throws  GrouperDAOException
1812   {
1813     String    attr;
1814     boolean   modified    = false;
1815     Element   e0;
1816     Iterator  it          = this._getInternalAttributes(e).iterator();
1817     while (it.hasNext()) {
1818       e0    = (Element) it.next();
1819       attr  = e0.getAttribute("name");
1820       if      ( "createSubject".equals(attr) ) {
1821         this._setCreateSubject(g, e0);
1822         modified = true;
1823       }
1824       else if ( "createTime".equals(attr) ) {
1825         if ( this._setCreateTime(g, e0) ) {
1826           modified = true;
1827         }
1828       }
1829     }
1830     if (modified) {
1831       GrouperDAOFactory.getFactory().getGroup().update( g);
1832     }
1833   } // private void _setInternalAttributesAttributes(g, e)
1834   
1835   // @since   1.2.0
1836   private void _setInternalAttributes(Stem ns, Element e) 
1837     throws  GrouperDAOException
1838   {
1839     String    attr;
1840     boolean   modified    = false;
1841     Element   e0;
1842     Iterator  it          = this._getInternalAttributes(e).iterator();
1843     while (it.hasNext()) {
1844       e0    = (Element) it.next();
1845       attr  = e0.getAttribute("name");
1846       if      ( "createSubject".equals(attr) ) {
1847         this._setCreateSubject(ns, e0);
1848         modified = true;
1849       }
1850       else if ( "createTime".equals(attr) ) {
1851         if ( this._setCreateTime(ns, e0) ) {
1852           modified = true;
1853         }
1854       }
1855     }
1856     if (modified) {
1857       ns.store();
1858     }
1859   } // private void _setInternalAttributesAttributes(ns, e)
1860 
1861   
1862   // GETTERS //
1863 
1864   // @since   1.1.0
1865   private Document _getDocument() {
1866     return this.doc;
1867   } // private Document _getDocument()
1868 
1869   // @since   1.1.0
1870   private boolean _getUpdateOnly() {
1871     return this.updateOnly;
1872   } // private boolean _getUpdateOnly()
1873 
1874 
1875   // SETTERS //
1876 
1877   // @since   1.1.0
1878   private void _setDocument(Document doc) 
1879     throws  IllegalArgumentException
1880   {
1881     if (doc == null) {
1882       throw new IllegalArgumentException(E.INVALID_DOC);
1883     }
1884     this.doc = doc;
1885   } // private void _setDocument(doc)
1886 
1887   // @since   1.1.0
1888   private void _setUpdateOnly(boolean updateOnly) {
1889     this.updateOnly = updateOnly;
1890   } // private void _setUpdateOnly(updateOnly)
1891 
1892   // @since 1.5.1
1893   /**
1894    * Return the value of the immediate child <description> element or null. Throws a
1895    * RuntimeException if there is more than one description found.
1896    * 
1897    * @param e
1898    *          the element
1899    * @return the description or null
1900    */
1901   private String _getDescription(Element e) {
1902     String desc = null;   
1903     Collection elements = this._getImmediateElements(e, GrouperConfig.ATTRIBUTE_DESCRIPTION);    
1904     if (elements.isEmpty()) {
1905       return desc;
1906     }
1907     if (elements.size() > 1) {
1908       throw new IllegalArgumentException(E.ELEMENT_NOT_UNIQUE + GrouperConfig.ATTRIBUTE_DESCRIPTION);
1909     }
1910     Element elDesc = (Element) elements.iterator().next();    
1911     if (elDesc != null) {
1912       try {
1913         desc = ((Text) elDesc.getFirstChild()).getData();
1914       } catch (NullPointerException npe) {
1915         // ignore
1916       }
1917     }
1918     return desc;
1919   }
1920   
1921 } // public class XmlImporter
1922