View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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  package org.kuali.rice.kew.xml;
17  
18  import org.jdom.Document;
19  import org.jdom.Element;
20  import org.jdom.JDOMException;
21  import org.kuali.rice.core.api.util.xml.XmlException;
22  import org.kuali.rice.core.api.util.xml.XmlHelper;
23  import org.kuali.rice.kew.api.KewApiConstants;
24  import org.kuali.rice.kew.util.Utilities;
25  import org.kuali.rice.kim.api.KimConstants;
26  import org.kuali.rice.kim.api.group.Group;
27  import org.kuali.rice.kim.api.group.GroupService;
28  import org.kuali.rice.kim.api.identity.IdentityService;
29  import org.kuali.rice.kim.api.identity.principal.Principal;
30  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
31  import org.kuali.rice.kim.api.type.KimType;
32  import org.kuali.rice.kim.api.type.KimTypeAttribute;
33  import org.xml.sax.SAXException;
34  
35  import javax.xml.parsers.ParserConfigurationException;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.util.ArrayList;
39  import java.util.HashMap;
40  import java.util.List;
41  import java.util.Map;
42  
43  import static org.kuali.rice.core.api.impex.xml.XmlConstants.*;
44  
45  
46  /**
47   * Parses groups from XML.
48   *
49   * @see Group
50   *
51   * @author Kuali Rice Team (rice.collab@kuali.org)
52   *
53   */
54  public class GroupXmlParser {
55      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(GroupXmlParser.class);
56      private static final boolean DEFAULT_ACTIVE_VALUE = true;
57      private static final String DEFAULT_GROUP_DESCRIPTION = "";
58      private HashMap<String, List<String>> memberGroupIds = new HashMap<String, List<String>>();
59      private HashMap<String, List<String>> memberGroupNames = new HashMap<String, List<String>>();
60      private HashMap<String, List<String>> memberPrincipalIds = new HashMap<String, List<String>>();
61      private Map<String, String> groupAttributes = new HashMap<String, String>();
62  
63      public List<Group> parseGroups(InputStream input) throws IOException, XmlException {
64          try {
65              Document doc = XmlHelper.trimSAXXml(input);
66              Element root = doc.getRootElement();
67              return parseGroups(root);
68          } catch (JDOMException e) {
69              throw new XmlException("Parse error.", e);
70          } catch (SAXException e){
71              throw new XmlException("Parse error.",e);
72          } catch(ParserConfigurationException e){
73              throw new XmlException("Parse error.",e);
74          }
75      }
76  
77  
78      /**
79       * Parses and saves groups
80       * @param element top-level 'data' element which should contain a <groups> child element
81       * @return a list of parsed and saved, current, groups;
82       * @throws XmlException
83       */
84      @SuppressWarnings("unchecked")
85  	public List<Group> parseGroups(Element element) throws XmlException {
86          List<Group> groups = new ArrayList<Group>();
87          for (Element groupsElement: (List<Element>) element.getChildren(GROUPS, GROUP_NAMESPACE)) {
88  
89              for (Element groupElement: (List<Element>) groupsElement.getChildren(GROUP, GROUP_NAMESPACE)) {
90                  groups.add(parseGroup(groupElement));
91              }
92          }
93          for (Group group : groups) {
94              GroupService groupService = KimApiServiceLocator.getGroupService();
95              // check if group already exists
96              Group foundGroup = groupService.getGroupByNamespaceCodeAndName(group.getNamespaceCode(), group.getName());
97  
98              if (foundGroup == null) {
99                  if ( LOG.isInfoEnabled() ) {
100                 	LOG.info("Group named '" + group.getName() + "' not found, creating new group named '" + group.getName() + "'");
101                 }
102                 try {
103                     Group newGroup =  groupService.createGroup(group);
104 
105                     String key = newGroup.getNamespaceCode().trim() + KewApiConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + newGroup.getName().trim();
106                     addGroupMembers(newGroup, key);
107                 } catch (Exception e) {
108                     throw new RuntimeException("Error creating group with name '" + group.getName() + "'", e);
109                 }
110             } else {
111             	if ( LOG.isInfoEnabled() ) {
112             		LOG.info("Group named '" + group.getName() + "' found, creating a new version");
113             	}
114                 try {
115                     Group.Builder builder = Group.Builder.create(foundGroup);
116                     builder.setActive(group.isActive());
117                     builder.setDescription(group.getDescription());
118                     builder.setKimTypeId(group.getKimTypeId());
119 
120                     //builder.setVersionNumber(foundGroup.getVersionNumber());
121                     group = builder.build();
122                     groupService.updateGroup(foundGroup.getId(), group);
123 
124                     //delete existing group members and replace with new
125                     groupService.removeAllMembers(foundGroup.getId());
126 
127                     String key = group.getNamespaceCode().trim() + KewApiConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + group.getName().trim();
128                     addGroupMembers(group, key);
129 
130                 } catch (Exception e) {
131                     throw new RuntimeException("Error updating group.", e);
132                 }
133             }
134         }
135         return groups;
136     }
137 
138     @SuppressWarnings("unchecked")
139 	private Group parseGroup(Element element) throws XmlException {
140 
141 
142         // Type element and children (namespace and name)
143 
144         String typeId = null;
145         KimType kimTypeInfo;
146         List<KimTypeAttribute> kimTypeAttributes = new ArrayList<KimTypeAttribute>();
147         if (element.getChild(TYPE, GROUP_NAMESPACE) != null) {
148             Element typeElement = element.getChild(TYPE, GROUP_NAMESPACE);
149             String typeNamespace = typeElement.getChildText(NAMESPACE, GROUP_NAMESPACE);
150             String typeName = typeElement.getChildText(NAME, GROUP_NAMESPACE);
151             kimTypeInfo = KimApiServiceLocator.getKimTypeInfoService().findKimTypeByNameAndNamespace(typeNamespace, typeName);
152             if (kimTypeInfo != null) {
153             	typeId = kimTypeInfo.getId();
154                 kimTypeAttributes = kimTypeInfo.getAttributeDefinitions();
155             } else  {
156                 throw new XmlException("Invalid type name and namespace specified.");
157             }
158         } else { //set to default type
159             kimTypeInfo = KimApiServiceLocator.getKimTypeInfoService().findKimTypeByNameAndNamespace(KimConstants.KIM_TYPE_DEFAULT_NAMESPACE, KimConstants.KIM_TYPE_DEFAULT_NAME);
160             if (kimTypeInfo != null) {
161             	typeId = kimTypeInfo.getId();
162                 kimTypeAttributes = kimTypeInfo.getAttributeDefinitions();
163             } else {
164             	throw new RuntimeException("Failed to locate the 'Default' group type!  Please ensure that it's in your database.");
165             }
166         }
167         //groupInfo.setKimTypeId(typeId);
168 
169         String groupNamespace = element.getChildText(NAMESPACE, GROUP_NAMESPACE);
170         if (groupNamespace == null) {
171             throw new XmlException("Namespace must have a value.");
172         }
173 
174         String groupName = element.getChildText(NAME, GROUP_NAMESPACE);
175         if (groupName == null) {
176             throw new XmlException("Name must have a value.");
177         }
178 
179         Group.Builder groupInfo = Group.Builder.create(groupNamespace, groupName, typeId);
180         IdentityService identityService = KimApiServiceLocator.getIdentityService();
181         //groupInfo.setGroupName(element.getChildText(NAME, GROUP_NAMESPACE));
182 
183         String id = element.getChildText(ID, GROUP_NAMESPACE);
184         if (id != null) {
185             groupInfo.setId(id.trim());
186         } else {
187         	
188         }
189 
190         String description = element.getChildText(DESCRIPTION, GROUP_NAMESPACE);
191         if (description != null && !description.trim().equals("")) {
192             groupInfo.setDescription(description);
193         }
194 
195         //Active Indicator
196         groupInfo.setActive(DEFAULT_ACTIVE_VALUE);
197         if (element.getChildText(ACTIVE, GROUP_NAMESPACE) != null) {
198             String active = element.getChildText(ACTIVE, GROUP_NAMESPACE).trim();
199             if (active.toUpperCase().equals("N") || active.toUpperCase().equals("FALSE")) {
200                 groupInfo.setActive(false);
201             }
202         }
203 
204         //Get list of attribute keys
205         List<String> validAttributeKeys = new ArrayList<String>();
206         for (KimTypeAttribute attribute : kimTypeAttributes) {
207             validAttributeKeys.add(attribute.getKimAttribute().getAttributeName());
208         }
209         //Group attributes
210         if (element.getChild(ATTRIBUTES, GROUP_NAMESPACE) != null) {
211             List<Element> attributes = element.getChild(ATTRIBUTES, GROUP_NAMESPACE).getChildren();
212 
213             Map<String, String> attrMap = new HashMap<String, String>();
214             for (Element attr : attributes ) {
215                 attrMap.put(attr.getAttributeValue(KEY), attr.getAttributeValue(VALUE));
216                 if (!validAttributeKeys.contains(attr.getAttributeValue(KEY))) {
217                     throw new XmlException("Invalid attribute specified.");
218                 }
219             }
220             Map<String, String> groupAttributes = attrMap;
221             if (!groupAttributes.isEmpty()) {
222                 groupInfo.setAttributes(groupAttributes);
223             }
224         }
225 
226         //Group members
227 
228         List<Element> members = null;
229         if(element.getChild(MEMBERS, GROUP_NAMESPACE) == null) {
230             members = new ArrayList<Element>();
231         }
232         else {
233             members = element.getChild(MEMBERS, GROUP_NAMESPACE).getChildren();
234         }
235         for (Element member : members) {
236             String elementName = member.getName().trim();
237             if (elementName.equals(PRINCIPAL_NAME)) {
238                 String principalName = member.getText().trim();
239                 Principal principal = identityService.getPrincipalByPrincipalName(principalName);
240                 if (principal != null) {
241                     addPrincipalToGroup(groupInfo.getNamespaceCode(), groupInfo.getName(), principal.getPrincipalId());
242                 } else {
243                     throw new XmlException("Principal Name "+principalName+" cannot be found.");
244                 }
245             } else if (elementName.equals(PRINCIPAL_ID)) {
246                 String xmlPrincipalId = member.getText().trim();
247                 Principal principal = identityService.getPrincipal(xmlPrincipalId);
248                 if (principal != null) {
249                     addPrincipalToGroup(groupInfo.getNamespaceCode(), groupInfo.getName(), principal.getPrincipalId());
250                 } else {
251                     throw new XmlException("Principal Id "+xmlPrincipalId+" cannot be found.");
252                 }
253             // Groups are handled differently since the member group may not be saved yet.  Therefore they need to be validated after the groups are saved.
254             } else if (elementName.equals(GROUP_ID)) {
255                 String xmlGroupId = member.getText().trim();
256                 addGroupToGroup(groupInfo.getNamespaceCode(), groupInfo.getName(), xmlGroupId);
257             } else if (elementName.equals(GROUP_NAME)) {
258                 String xmlGroupName = member.getChildText(NAME, GROUP_NAMESPACE).trim();
259                 String xmlGroupNamespace = member.getChildText(NAMESPACE, GROUP_NAMESPACE).trim();
260                 addGroupNameToGroup(groupInfo.getNamespaceCode(), groupInfo.getName(), xmlGroupNamespace, xmlGroupName);
261             } else {
262                 LOG.error("Unknown member element: " + elementName);
263             }
264 
265 
266         }
267 
268         return groupInfo.build();
269 
270     }
271 
272     private void addPrincipalToGroup(String groupNamespace, String groupName, String principalId) {
273         String key = groupNamespace.trim() + KewApiConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + groupName.trim();
274         List<String> principalIds = memberPrincipalIds.get(key);
275         if (principalIds == null) {
276             principalIds = new ArrayList<String>();
277         }
278         principalIds.add(principalId);
279         memberPrincipalIds.put(key, principalIds);
280     }
281 
282     private void addGroupToGroup(String groupNamespace, String groupName, String groupId) {
283         String key = groupNamespace.trim() + KewApiConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + groupName.trim();
284         List<String> groupIds = memberGroupIds.get(key);
285         if (groupIds == null) {
286             groupIds = new ArrayList<String>();
287         }
288         groupIds.add(groupId);
289         memberGroupIds.put(key, groupIds);
290     }
291 
292     private void addGroupNameToGroup(String groupNamespace, String groupName, String memberGroupNamespace, String memberGroupName) {
293         String key = groupNamespace.trim() + KewApiConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + groupName.trim();
294         List<String> groupNames = memberGroupNames.get(key);
295         if (groupNames == null) {
296             groupNames = new ArrayList<String>();
297         }
298         groupNames.add(memberGroupNamespace.trim() + KewApiConstants.KIM_GROUP_NAMESPACE_NAME_DELIMITER_CHARACTER + memberGroupName.trim());
299         memberGroupNames.put(key, groupNames);
300     }
301 
302     private void addGroupMembers(Group groupInfo, String key) throws XmlException {
303         GroupService groupService = KimApiServiceLocator.getGroupService();
304         List<String> groupIds = memberGroupIds.get(key);
305         if (groupIds != null) {
306             for (String groupId : groupIds) {
307                 Group group = groupService.getGroup(groupId);
308                 if (group != null) {
309                     groupService.addGroupToGroup(group.getId(), groupInfo.getId());
310                 } else {
311                     throw new XmlException("Group Id "+groupId+" cannot be found.");
312                 }
313             }
314         }
315         List<String> groupNames = memberGroupNames.get(key);
316         if (groupNames != null) {
317             for (String groupName : groupNames) {
318                 Group group = groupService.getGroupByNamespaceCodeAndName(Utilities.parseGroupNamespaceCode(groupName),
319                         Utilities.parseGroupName(groupName));
320                 if (group != null) {
321                 	groupService.addGroupToGroup(group.getId(), groupInfo.getId());
322                 } else {
323                     throw new XmlException("Group "+groupName+" cannot be found.");
324                 }
325             }
326         }
327         List<String> principalIds = memberPrincipalIds.get(key);
328         if (principalIds != null) {
329             for (String principalId : principalIds) {
330             	groupService.addPrincipalToGroup(principalId, groupInfo.getId());
331             }
332         }
333 
334     }
335 }