View Javadoc

1   /*
2    * Copyright 2007-2010 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.kim.service.impl;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.collections.Predicate;
20  import org.apache.commons.lang.StringUtils;
21  import org.apache.log4j.Logger;
22  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
23  import org.kuali.rice.core.api.exception.RiceIllegalStateException;
24  import org.kuali.rice.core.api.exception.RiceRuntimeException;
25  import org.kuali.rice.kim.api.group.Group;
26  import org.kuali.rice.kim.api.group.GroupService;
27  import org.kuali.rice.kim.api.group.GroupUpdateService;
28  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
29  import org.kuali.rice.kim.impl.common.attribute.KimAttributeDataBo;
30  import org.kuali.rice.kim.impl.group.GroupAttributeBo;
31  import org.kuali.rice.kim.impl.group.GroupBo;
32  import org.kuali.rice.kim.impl.group.GroupMemberBo;
33  import org.kuali.rice.kim.impl.group.GroupServiceBase;
34  import org.kuali.rice.kim.service.IdentityManagementNotificationService;
35  import org.kuali.rice.kim.service.KIMServiceLocatorInternal;
36  import org.kuali.rice.kim.util.KIMPropertyConstants;
37  import org.kuali.rice.kim.util.KIMWebServiceConstants;
38  import org.kuali.rice.kim.util.KimConstants.KimGroupMemberTypes;
39  import org.kuali.rice.krad.service.KRADServiceLocator;
40  import org.kuali.rice.krad.service.SequenceAccessorService;
41  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
42  
43  import javax.jws.WebService;
44  import javax.xml.namespace.QName;
45  import java.util.ArrayList;
46  import java.util.Collection;
47  import java.util.HashMap;
48  import java.util.List;
49  import java.util.Map;
50  
51  /**
52   * This is the default implementation for the {@link GroupUpdateService}, where the write methods for KIM groups are located.
53   *
54   * @author Kuali Rice Team (rice.collab@kuali.org)
55   *
56   */
57  @WebService(endpointInterface = KIMWebServiceConstants.GroupUpdateService.INTERFACE_CLASS, serviceName = KIMWebServiceConstants.GroupUpdateService.WEB_SERVICE_NAME, portName = KIMWebServiceConstants.GroupUpdateService.WEB_SERVICE_PORT, targetNamespace = KIMWebServiceConstants.MODULE_TARGET_NAMESPACE)
58  public class GroupUpdateServiceImpl extends GroupServiceBase implements GroupUpdateService {
59  	
60  	private static final Logger LOG = Logger.getLogger(GroupUpdateServiceImpl.class);
61  
62  	/**
63       * @see org.kuali.rice.kim.api.group.GroupUpdateService#addGroupToGroup(java.lang.String, java.lang.String)
64       */
65      public boolean addGroupToGroup(String childId, String parentId) {
66          if(childId.equals(parentId)) {
67              throw new RiceIllegalArgumentException("Can't add group to itself.");
68          }
69  
70          if(isGroupMemberOfGroup(parentId, childId)) {
71              throw new RiceIllegalArgumentException("Circular group reference.");
72          }
73  
74          GroupMemberBo groupMember = new GroupMemberBo();
75          groupMember.setGroupId(parentId);
76          groupMember.setTypeCode(KimGroupMemberTypes.GROUP_MEMBER_TYPE);
77          groupMember.setMemberId(childId);
78  
79          this.businessObjectService.save(groupMember);
80          getIdentityManagementNotificationService().groupUpdated();
81  
82          return true;
83      }
84  
85      /**
86       * @see org.kuali.rice.kim.api.group.GroupUpdateService#addPrincipalToGroup(java.lang.String, java.lang.String)
87       */
88      public boolean addPrincipalToGroup(String principalId, String groupId) {
89          GroupMemberBo groupMember = new GroupMemberBo();
90          groupMember.setGroupId(groupId);
91          groupMember.setTypeCode(KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE);
92          groupMember.setMemberId(principalId);
93  
94          groupMember = (GroupMemberBo)this.businessObjectService.save(groupMember);
95          KIMServiceLocatorInternal.getGroupInternalService().updateForUserAddedToGroup(groupMember.getMemberId(), groupMember.getGroupId());
96          getIdentityManagementNotificationService().groupUpdated();
97          return true;
98      }
99  
100     public Group createGroup(Group group) {
101         if (group == null) {
102             throw new RiceIllegalArgumentException(("group is null"));
103         }
104         if (StringUtils.isNotBlank(group.getId()) && getGroup(group.getId()) != null) {
105             throw new RiceIllegalStateException("the group to create already exists: " + group);
106         }
107         List<GroupAttributeBo> attrBos = KimAttributeDataBo.createFrom(GroupAttributeBo.class, group.getAttributes(), group.getKimTypeId());
108         if (StringUtils.isNotEmpty(group.getId())) {
109             for (GroupAttributeBo attr : attrBos) {
110                 attr.setAssignedToId(group.getId());
111             }
112         }
113         GroupBo bo = GroupBo.from(group);
114         bo.setAttributeDetails(attrBos);
115 
116         bo = saveGroup(bo);
117 
118         return GroupBo.to(bo);
119     }
120 
121     public Group updateGroup(Group group) {
122         if (group == null) {
123             throw new RiceIllegalArgumentException(("group is null"));
124         }
125         GroupBo origGroup = getGroupBo(group.getId());
126         if (StringUtils.isBlank(group.getId()) || origGroup == null) {
127             throw new RiceIllegalStateException("the group does not exist: " + group);
128         }
129         List<GroupAttributeBo> attrBos = KimAttributeDataBo.createFrom(GroupAttributeBo.class, group.getAttributes(), group.getKimTypeId());
130         GroupBo bo = GroupBo.from(group);
131         bo.setMembers(origGroup.getMembers());
132         bo.setAttributeDetails(attrBos);
133 
134         bo = saveGroup(bo);
135 
136         return GroupBo.to(bo);
137     }
138 
139     	/**
140 	 *
141 	 * @see org.kuali.rice.kim.api.group.GroupUpdateService#updateGroup(java.lang.String, org.kuali.rice.kim.api.group.Group)
142 	 */
143 	public Group updateGroup(String groupId, Group group) {
144         if (group == null) {
145             throw new RiceIllegalArgumentException(("group is null"));
146         }
147         if (StringUtils.isEmpty(groupId)) {
148             throw new RiceIllegalArgumentException(("groupId is empty"));
149         }
150 
151         if (StringUtils.equals(groupId, group.getId())) {
152             return updateGroup(group);
153         }
154 
155         //if group Ids are different, inactivate old group, and create new with new id based off old
156         GroupBo groupBo = getGroupBo(groupId);
157 
158         if (StringUtils.isBlank(group.getId()) || groupBo == null) {
159             throw new RiceIllegalStateException("the group does not exist: " + group);
160         }
161 
162         //create and save new group
163         GroupBo newGroup = GroupBo.from(group);
164         newGroup.setMembers(groupBo.getMembers());
165         List<GroupAttributeBo> attrBos = KimAttributeDataBo.createFrom(GroupAttributeBo.class, group.getAttributes(), group.getKimTypeId());
166         newGroup.setAttributeDetails(attrBos);
167         newGroup = saveGroup(newGroup);
168 
169         //inactivate and save old group
170         groupBo.setActive(false);
171         saveGroup(groupBo);
172 
173         return GroupBo.to(newGroup);
174     }
175 
176     /**
177     *
178     * @see org.kuali.rice.kim.api.group.GroupUpdateService#removeAllMembers(java.lang.String)
179     */
180    public void removeAllMembers(String groupId) {
181 	   GroupService groupService = KimApiServiceLocator.getGroupService();
182        List<String> memberPrincipalsBefore = groupService.getMemberPrincipalIds(groupId);
183 
184        Collection<GroupMemberBo> toDeactivate = getActiveGroupMembers(groupId, null, null);
185        java.sql.Timestamp today = new java.sql.Timestamp(System.currentTimeMillis());
186 
187        // Set principals as inactive
188         for (GroupMemberBo aToDeactivate : toDeactivate) {
189             aToDeactivate.setActiveToDate(today);
190         }
191 
192        // Save
193        this.businessObjectService.save(new ArrayList<GroupMemberBo>(toDeactivate));
194        List<String> memberPrincipalsAfter = groupService.getMemberPrincipalIds(groupId);
195 
196        if (!CollectionUtils.isEmpty(memberPrincipalsAfter)) {
197     	   // should never happen!
198     	   LOG.warn("after attempting removal of all members, group with id '" + groupId + "' still has principal members");
199        }
200 
201        // do updates
202        KIMServiceLocatorInternal.getGroupInternalService().updateForWorkgroupChange(groupId, memberPrincipalsBefore, memberPrincipalsAfter);
203        getIdentityManagementNotificationService().groupUpdated();
204    }
205 
206 	/**
207      * @see org.kuali.rice.kim.api.group.GroupUpdateService#removeGroupFromGroup(java.lang.String, java.lang.String)
208      */
209     public boolean removeGroupFromGroup(String childId, String parentId) {
210     	java.sql.Timestamp today = new java.sql.Timestamp(System.currentTimeMillis());
211 
212     	List<GroupMemberBo> groupMembers =
213     		getActiveGroupMembers(parentId, childId, KimGroupMemberTypes.GROUP_MEMBER_TYPE);
214 
215         if(groupMembers.size() == 1) {
216         	GroupMemberBo groupMember = groupMembers.get(0);
217         	groupMember.setActiveToDate(today);
218             this.businessObjectService.save(groupMember);
219             getIdentityManagementNotificationService().groupUpdated();
220             return true;
221         }
222 
223         return false;
224     }
225 
226 	/**
227      * @see org.kuali.rice.kim.api.group.GroupUpdateService#removePrincipalFromGroup(java.lang.String, java.lang.String)
228      */
229     @SuppressWarnings("unchecked")
230     public boolean removePrincipalFromGroup(String principalId, String groupId) {
231     	List<GroupMemberBo> groupMembers =
232     		getActiveGroupMembers(groupId, principalId, KimGroupMemberTypes.PRINCIPAL_MEMBER_TYPE);
233 
234         if(groupMembers.size() == 1) {
235         	GroupMemberBo member = groupMembers.iterator().next();
236         	member.setActiveToDate(new java.sql.Timestamp(System.currentTimeMillis()));
237         	this.businessObjectService.save(member);
238             KIMServiceLocatorInternal.getGroupInternalService().updateForUserRemovedFromGroup(member.getMemberId(), member.getGroupId());
239             getIdentityManagementNotificationService().groupUpdated();
240             return true;
241         }
242 
243         return false;
244     }
245 
246 	protected GroupBo saveGroup(GroupBo group) {
247 		if ( group == null ) {
248 			return null;
249 		} else if (group.getId() != null) {
250 			// Get the version of the group that is in the DB
251 			GroupBo oldGroup = getGroupBo(group.getId());
252 
253 			if (oldGroup != null) {
254 				// Inactivate and re-add members no longer in the group (in order to preserve history).
255 				java.sql.Timestamp activeTo = new java.sql.Timestamp(System.currentTimeMillis());
256 				List<GroupMemberBo> toReAdd = null;
257 
258 				if (oldGroup.getMembers() != null) {
259                     for (GroupMemberBo member : oldGroup.getMembers()) {
260                         // if the old member isn't in the new group
261                         if (group.getMembers() == null || !group.getMembers().contains(member)) {
262                             // inactivate the member
263                             member.setActiveToDate(activeTo);
264                             if (toReAdd == null) {
265                                 toReAdd = new ArrayList<GroupMemberBo>();
266                             }
267                             // queue it up for re-adding
268                             toReAdd.add(member);
269                         }
270                     }
271 				}
272 
273 				// do the re-adding
274 				if (toReAdd != null) {
275 					List<GroupMemberBo> groupMembers = group.getMembers();
276 					if (groupMembers == null) {
277                         groupMembers = new ArrayList<GroupMemberBo>(toReAdd.size());
278                     }
279 					group.setMembers(groupMembers);
280 				}
281 			}
282 		}
283 
284 		GroupBo savedGroup = KIMServiceLocatorInternal.getGroupInternalService().saveWorkgroup(group);
285 		getIdentityManagementNotificationService().groupUpdated();
286 		return savedGroup;
287 	}
288 
289 
290 	/**
291 	 * This helper method gets the active group members of the specified type (see {@link KimGroupMemberTypes}).
292 	 * If the optional params are null, it will return all active members for the specified group regardless
293 	 * of type.
294 	 *
295 	 * @param parentId
296 	 * @param childId optional, but if provided then memberType must be too
297 	 * @param memberType optional, but must be provided if childId is
298      * @return a list of group members
299 	 */
300 	private List<GroupMemberBo> getActiveGroupMembers(String parentId,
301 			String childId, String memberType) {
302     	final java.sql.Date today = new java.sql.Date(System.currentTimeMillis());
303 
304     	if (childId != null && memberType == null) throw new RiceRuntimeException("memberType must be non-null if childId is non-null");
305 
306 		Map<String,Object> criteria = new HashMap<String,Object>(4);
307         criteria.put(KIMPropertyConstants.GroupMember.GROUP_ID, parentId);
308 
309         if (childId != null) {
310         	criteria.put(KIMPropertyConstants.GroupMember.MEMBER_ID, childId);
311         	criteria.put(KIMPropertyConstants.GroupMember.MEMBER_TYPE_CODE, memberType);
312         }
313 
314         Collection<GroupMemberBo> groupMembers = this.businessObjectService.findMatching(GroupMemberBo.class, criteria);
315 
316         CollectionUtils.filter(groupMembers, new Predicate() {
317 			public boolean evaluate(Object object) {
318 				GroupMemberBo member = (GroupMemberBo) object;
319 				// keep in the collection (return true) if the activeToDate is null, or if it is set to a future date
320 				return member.getActiveToDate() == null || today.before(member.getActiveToDate());
321 			}
322 		});
323 
324         return new ArrayList<GroupMemberBo>(groupMembers);
325 	}
326 
327     protected IdentityManagementNotificationService getIdentityManagementNotificationService() {
328         return (IdentityManagementNotificationService) KsbApiServiceLocator.getMessageHelper().getServiceAsynchronously(new QName("KIM", "kimIdentityManagementNotificationService"));
329     }
330 	
331 }