View Javadoc

1   /*
2    * Copyright 2007-2008 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 com.google.common.collect.Maps;
19  import org.junit.Test;
20  import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
21  import org.kuali.rice.kim.api.KimApiConstants;
22  import org.kuali.rice.kim.api.role.Role;
23  import org.kuali.rice.kim.impl.common.delegate.DelegateBo;
24  import org.kuali.rice.kim.impl.common.delegate.DelegateMemberBo;
25  import org.kuali.rice.kim.impl.role.RoleBo;
26  import org.kuali.rice.kim.impl.role.RoleMemberBo;
27  import org.kuali.rice.kim.impl.role.RoleServiceImpl;
28  import org.kuali.rice.kim.test.KIMTestCase;
29  import org.kuali.rice.krad.bo.PersistableBusinessObjectBase;
30  
31  import javax.xml.namespace.QName;
32  import java.sql.Timestamp;
33  import java.util.ArrayList;
34  import java.util.Arrays;
35  import java.util.Collections;
36  import java.util.HashMap;
37  import java.util.List;
38  import java.util.Map;
39  
40  import static org.junit.Assert.*;
41  
42  /**
43   * This is a description of what this class does - shyu don't forget to fill this in. 
44   * 
45   * @author Kuali Rice Team (rice.collab@kuali.org)
46   *
47   */
48  public class RoleServiceImplTest extends KIMTestCase {
49  
50  	private RoleServiceImpl roleService;
51  
52  	public void setUp() throws Exception {
53  		super.setUp();
54  		roleService = (RoleServiceImpl) GlobalResourceLoader.getService(
55                  new QName(KimApiConstants.Namespaces.KIM_NAMESPACE_2_0, KimApiConstants.ServiceNames.ROLE_SERVICE_SOAP));
56  	}
57  
58  	@Test
59  	public void testPrincipaHasRoleOfDirectAssignment() {
60  		List <String>roleIds = new ArrayList<String>();
61  		roleIds.add("r1");
62  		assertTrue( "p1 has direct role r1", roleService.principalHasRole("p1", roleIds, null ));	
63  		//assertFalse( "p4 has no direct/higher level role r1", roleService.principalHasRole("p4", roleIds, null ));
64  		Map<String, String> qualification = new HashMap<String, String>();
65  		qualification.put("Attribute 2", "CHEM");
66  		assertTrue( "p1 has direct role r1 with rp2 attr data", roleService.principalHasRole("p1", roleIds, qualification));
67  		qualification.clear();
68  		//requested qualification rolls up to a higher element in some hierarchy 
69  		// method not implemented yet, not quite clear how this works
70  		qualification.put("Attribute 3", "PHYS");
71  		assertTrue( "p1 has direct role r1 with rp2 attr data", roleService.principalHasRole("p1", roleIds, Maps.newHashMap(
72                  qualification)));
73  	}
74  
75  	@Test
76  	public void testPrincipalHasRoleOfHigherLevel() {
77  		// "p3" is in "r2" and "r2 contains "r1"
78  		List <String>roleIds = new ArrayList<String>();
79  		roleIds.add("r2");
80  		assertTrue( "p1 has assigned in higher level role r1", roleService.principalHasRole("p1", roleIds, null ));		
81  	}
82  	
83  	@Test
84  	public void testPrincipalHasRoleContainsGroupAssigned() {
85  		// "p2" is in "g1" and "g1" assigned to "r2"
86  		List <String>roleIds = new ArrayList<String>();
87  		roleIds.add("r2");
88  		assertTrue( "p2 is assigned to g1 and g1 assigned to r2", roleService.principalHasRole("p2", roleIds, null ));		
89  	}
90  
91  	/**
92  	 * Tests to ensure that the RoleServiceImpl's caching mechanisms are behaving as expected.
93  	 * 
94  	 * @throws Exception
95  	 */
96  	@Test
97  	public void testCachingBehavesCorrectly() throws Exception {
98  		RoleServiceTestImpl roleServiceTestImpl = new RoleServiceTestImpl();
99  		roleServiceTestImpl.setRoleDao(roleService.getRoleDao());
100 		
101 		// Ensure that read-only searching operations function properly for the role-related and delegation-related caches.
102 		roleServiceTestImpl.assertRoleCachingWorksAsExpected();
103 		roleServiceTestImpl.assertDelegationCachingWorksAsExpected();
104 		
105 		// Ensure that modifications to role-related and delegation-related objects will clear out the appropriate caches.
106 		roleServiceTestImpl.assertCachesAreClearedOnUpdatesAsExpected();
107 		roleServiceTestImpl.assertCachesAreRefreshedAsExpected();
108 	}
109 	
110 	/**
111 	 * Tests to ensure that a circular role membership cannot be created via the RoleService.
112 	 * 
113 	 * @throws Exception
114 	 */
115 	@Test (expected=IllegalArgumentException.class)
116 	public void testCircularRoleAssignment() {
117 		Map<String, String> map = new HashMap<String, String>();
118 		List <String>roleIds = new ArrayList<String>();
119 		roleIds.add("r1");
120 		roleService.assignRoleToRole("r5", "AUTH_SVC_TEST2", "RoleThree", map);
121 	}
122 	
123 	/**
124 	 * A convenience interface for reducing duplicated code when comparing KIM objects and testing certain KIM-object-caching capabilities.
125 	 * 
126 	 * @author Kuali Rice Team (rice.collab@kuali.org)
127 	 */
128 	private interface KimObjectTestChecker<T extends PersistableBusinessObjectBase> {
129 		/* Gets the simple class name of the KIM object, mostly for usage in error messages. */
130 		public String getKimObjectName();
131 		/* Gets the ID of the KIM object. */
132 		public String getKimObjectId(T kimObject);
133 		/* Used to compare certain fields on a KIM object for equality. */
134 		public void assertKimObjectsAreEqual(T oldKimObject, T newKimObject) throws Exception;
135 		/* Used to retrieve a KIM object from the cache based on its ID. */
136 		public T getKimObjectFromCacheById(String kimObjectId);
137 		/* Used to retrieve a KIM object from the RoleService based on its ID. */
138 		public T getKimObjectById(String kimObjectId);
139 		/* Indicates whether or not the KIM object is expected to be cleared from the cache when the delegation cache is emptied out. */
140 		public boolean isUnaffectedByClearingDelegationCache();
141 	}
142 
143 	/**
144 	 * A subclass of RoleServiceImpl for providing a convenient way to test the RoleServiceImpl's caching.
145 	 * For testing purposes, this subclass also overrides the shouldCacheMembersOfRole() method so that it will not cache the members or role "r6".
146 	 * 
147 	 * @author Kuali Rice Team (rice.collab@kuali.org)
148 	 */
149 	private final class RoleServiceTestImpl extends RoleServiceImpl {
150 		
151 		// Used for checking the caching capabilities of RoleImpl objects.
152 		private final KimObjectTestChecker<RoleBo> ROLE_IMPL_CHECKER = new KimObjectTestChecker<RoleBo>() {
153 			public String getKimObjectName() { return "RoleBo"; }
154 			public String getKimObjectId(RoleBo role) { return role.getId(); }
155 			public void assertKimObjectsAreEqual(RoleBo oldRole, RoleBo newRole) throws Exception {
156 				assertEquals("Role IDs do not match.", oldRole.getId(), newRole.getId());
157 				assertEquals("KIM type IDs do not match.", oldRole.getKimTypeId(), newRole.getKimTypeId());
158 				assertEquals("Namespace codes do not match.", oldRole.getNamespaceCode(), newRole.getNamespaceCode());
159 				assertEquals("Role names do not match.", oldRole.getName(), newRole.getName());
160 				assertEquals("Role descriptions do not match.", oldRole.getDescription(), newRole.getDescription());
161 				assertEquals("Role active statuses do not match.", oldRole.isActive(), newRole.isActive());
162 			}
163 			public RoleBo getKimObjectFromCacheById(String roleId) { return getRoleFromCache(roleId); }
164 			public RoleBo getKimObjectById(String roleId) { return getRoleBo(roleId); }
165 			public boolean isUnaffectedByClearingDelegationCache() { return true; }
166 		};
167 		
168 		// Used for checking the caching capabilities of RoleMemberImpl objects.
169 		private final KimObjectTestChecker<RoleMemberBo> ROLE_MEMBER_IMPL_CHECKER = new KimObjectTestChecker<RoleMemberBo>() {
170 			public String getKimObjectName() { return "RoleMemberBo"; }
171 			public String getKimObjectId(RoleMemberBo roleMember) { return roleMember.getRoleMemberId(); }
172 			public void assertKimObjectsAreEqual(RoleMemberBo oldMember, RoleMemberBo newMember) throws Exception {
173 				assertEquals("Role member IDs do not match.", oldMember.getRoleMemberId(), newMember.getRoleMemberId());
174 				assertEquals("Role IDs do not match.", oldMember.getRoleId(), newMember.getRoleId());
175 				assertEquals("Role members' member IDs do not match.", oldMember.getMemberId(), newMember.getMemberId());
176 				assertEquals("Role members' member type codes do not match.", oldMember.getMemberTypeCode(), newMember.getMemberTypeCode());
177 				assertEquals("Role member active statuses do not match.", oldMember.isActive(
178                         new Timestamp(System.currentTimeMillis())), newMember.isActive(new Timestamp(System.currentTimeMillis())));
179 			}
180 			public RoleMemberBo getKimObjectFromCacheById(String roleMemberId) { return getRoleMemberFromCache(roleMemberId); }
181 			public RoleMemberBo getKimObjectById(String roleMemberId) { return getRoleMemberBo(roleMemberId); }
182 			public boolean isUnaffectedByClearingDelegationCache() { return true; }
183 		};
184 		
185 		// Used for checking the caching capabilities of KimDelegationImpl objects.
186 		private final KimObjectTestChecker<DelegateBo> KIM_DELEGATION_IMPL_CHECKER = new KimObjectTestChecker<DelegateBo>() {
187 			public String getKimObjectName() { return "DelegateBo"; }
188 			public String getKimObjectId(DelegateBo delegation) { return delegation.getDelegationId(); }
189 			public void assertKimObjectsAreEqual(DelegateBo oldDelegation, DelegateBo newDelegation) throws Exception {
190 				assertEquals("Delegation IDs do not match.", oldDelegation.getDelegationId(), newDelegation.getDelegationId());
191 				assertEquals("Role IDs do not match.", oldDelegation.getRoleId(), newDelegation.getRoleId());
192 				assertEquals("KIM type IDs do not match.", oldDelegation.getKimTypeId(), newDelegation.getKimTypeId());
193 				assertEquals("Delegation type codes do not match.", oldDelegation.getDelegationTypeCode(), newDelegation.getDelegationTypeCode());
194 				assertEquals("Delegation active statuses do not match.", oldDelegation.isActive(), newDelegation.isActive());
195 			}
196 			public DelegateBo getKimObjectFromCacheById(String delegationId) { return getDelegationFromCache(delegationId); }
197 			public DelegateBo getKimObjectById(String delegationId) { return getKimDelegationImpl(delegationId); }
198 			public boolean isUnaffectedByClearingDelegationCache() { return false; }
199 		};
200 		
201 		// Used for checking the caching capabilities of KimDelegationMemberImpl objects.
202 		private final KimObjectTestChecker<DelegateMemberBo> KIM_DLGN_MBR_IMPL_CHECKER = new KimObjectTestChecker<DelegateMemberBo>() {
203 			public String getKimObjectName() { return "DelegateMemberBo"; }
204 			public String getKimObjectId(DelegateMemberBo dlgnMember) { return dlgnMember.getDelegationMemberId(); }
205 			public void assertKimObjectsAreEqual(DelegateMemberBo oldMember, DelegateMemberBo newMember) throws Exception {
206 				assertEquals("Delegation member IDs do not match.", oldMember.getDelegationMemberId(), newMember.getDelegationMemberId());
207 				assertEquals("Delegation IDs do not match.", oldMember.getDelegationMemberId(), newMember.getDelegationMemberId());
208 				assertEquals("Delegation members' role member IDs do not match.", oldMember.getRoleMemberId(), newMember.getRoleMemberId());
209 				assertEquals("Delegation members' member IDs do not match.", oldMember.getMemberId(), newMember.getMemberId());
210 				assertEquals("Delegation members' member type codes do not match.", oldMember.getTypeCode(), newMember.getTypeCode());
211 				assertEquals("Delegation member active statuses do not match.", oldMember.isActive(
212                         new Timestamp(System.currentTimeMillis())), newMember.isActive(new Timestamp(System.currentTimeMillis())));
213 			}
214 			public DelegateMemberBo getKimObjectFromCacheById(String delegationMemberId) { return getDelegationMemberFromCache(delegationMemberId); }
215 			public DelegateMemberBo getKimObjectById(String delegationMemberId) { return getDelegateMemberBo(delegationMemberId); }
216 			public boolean isUnaffectedByClearingDelegationCache() { return false; }
217 		};
218 		
219 		/*
220 		 * Just for testing, this method has been overridden so that it will return false for the role ID "r6".
221 		 */
222 		@Override
223 		protected boolean shouldCacheMembersOfRole(String roleId) {
224 			return super.shouldCacheMembersOfRole(roleId) && !"r6".equals(roleId);
225 		}
226 		
227 		// --------------------------------------------------------------------------------------------------------------------
228 		// Methods related to testing read-only search operations involving the cache.
229 		// --------------------------------------------------------------------------------------------------------------------
230 		
231 		/*
232 		 * Tests the searches on the RoleImpl and RoleMemberImpl caches.
233 		 */
234 		private void assertRoleCachingWorksAsExpected() throws Exception {
235 			List<String> roleIds = Arrays.asList(new String[] {"r3", "r4", "r5", "r6"});
236 			String[][] roleNames = { {"AUTH_SVC_TEST2","RoleThree"}, {"AUTH_SVC_TEST2","RoleFour"},
237 					{"AUTH_SVC_TEST2","RoleFive"},{"AUTH_SVC_TEST2","RoleSix"} };
238 			
239 			// Ensure that by-ID caching of individual roles is working properly.
240 			assertKimObjectCachingByIdIsWorking(roleIds, roleIds, ROLE_IMPL_CHECKER);
241 			
242 			// Ensure that roles can be obtained properly from the cache by name.
243 			Map<String,RoleBo> firstRoleMap = getRoleImplMapByName(roleNames);
244 			assertRolesAreCachedByNameAsExpected(roleNames, true);
245 			Map<String,RoleBo> secondRoleMap = getRoleImplMapByName(roleNames);
246 			assertKimObjectResultsAreEqual(roleIds.size(), firstRoleMap, secondRoleMap, ROLE_IMPL_CHECKER);
247 			// Ensure that roles cached by name can be cleared properly.
248 			getIdentityManagementNotificationService().roleUpdated();
249 			assertRolesAreCachedByNameAsExpected(roleNames, false);
250 			firstRoleMap = getRoleImplMapByName(roleNames);
251 			assertKimObjectResultsAreEqual(roleIds.size(), secondRoleMap, firstRoleMap, ROLE_IMPL_CHECKER);
252 			// Clean up the cache.
253 			getIdentityManagementNotificationService().roleUpdated();
254 			
255 			// ----------------------------------------------------------------------------------------------------------------
256 			
257 			String[] cachedRoleMemberIdsArray = {"r3p5", "r4p10", "r4g5", "r5p6", "r5g6", "r5r3"};
258 			String[] allRoleMemberIdsArray = {"r3p5", "r4p10", "r4g5", "r5p6", "r5g6", "r5r3", "r6p9", "r6r4"};
259 			List<String> groupIds = Arrays.asList(new String[] {"g5","g6"});
260 			List<String> oneGroupId = Collections.singletonList("g5");
261 			List<String> oneRoleId = Collections.singletonList("r3");
262 			List<String> oneRoleId2 = Collections.singletonList("r5");
263 			List<String> oneRoleId3 = Collections.singletonList("r6");
264 			List<String> oneRoleId4 = Collections.singletonList("r4");
265 			RoleDaoAction[] daoActions = { RoleDaoAction.ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS,
266 					RoleDaoAction.ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS, RoleDaoAction.ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS,
267 							RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS, RoleDaoAction.ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS };
268 			
269 			String[][] results = { {"r3p5"}, {"r5p6"}, {"r3p5","r4p10","r5p6"}, {"r4g5","r5g6"}, {"r4g5"}, {"r5g6"}, {"r5r3"},
270 					{"r4p10","r4g5","r5r3"}, {"r4p10","r4g5","r5g6","r5r3"}, {"r5g6","r5r3"} };
271 			String[][] allResults = {{"r6p9"}, {"r3p5","r4p10","r5p6","r6p9"}, {"r5r3","r6r4"}, {"r6r4"},
272 					{"r4p10","r4g5","r5r3","r6r4"}, {"r4p10","r4g5","r5g6","r5r3","r6r4"}};
273 			
274 			// Ensure that by-ID caching of individual role members is working properly.
275 			assertKimObjectCachingByIdIsWorking(Arrays.asList(cachedRoleMemberIdsArray), Arrays.asList(allRoleMemberIdsArray), ROLE_MEMBER_IMPL_CHECKER);
276 			
277 			// Ensure that lists of role members can be cached properly. Note that the role members "r6p9" and "r6r4" will never be cached
278 			// because this class intentionally blocks the members of role "r6" from being cached; however, they should still be in the search results.
279 			assertRoleMemberListCachingIsWorking(daoActions[0], roleIds, "p5", null, null, results[0], results[0]);
280 			assertRoleMemberListCachingIsWorking(daoActions[0], roleIds, "p6", null, null, results[1], results[1]);
281 			assertRoleMemberListCachingIsWorking(daoActions[0], oneRoleId3, "p9", null, null, new String[]{}, allResults[0]);
282 			assertRoleMemberListCachingIsWorking(daoActions[0], roleIds, null, null, null, results[2], allResults[1]);
283 			assertRoleMemberListCachingIsWorking(daoActions[0], oneRoleId2, null, null, null, results[1], results[1]);
284 			assertRoleMemberListCachingIsWorking(daoActions[0], null, "p5", null, null, results[0], results[0]);
285 			assertRoleMemberListCachingIsWorking(daoActions[1], roleIds, null, groupIds, null, results[3], results[3]);
286 			assertRoleMemberListCachingIsWorking(daoActions[1], roleIds, null, oneGroupId, null, results[4], results[4]);
287 			assertRoleMemberListCachingIsWorking(daoActions[1], null, null, groupIds, null, results[3], results[3]);
288 			assertRoleMemberListCachingIsWorking(daoActions[1], null, null, oneGroupId, null, results[4], results[4]);
289 			assertRoleMemberListCachingIsWorking(daoActions[1], roleIds, null, null, null, results[3], results[3]);
290 			assertRoleMemberListCachingIsWorking(daoActions[1], oneRoleId, null, null, null, new String[]{}, new String[]{});
291 			assertRoleMemberListCachingIsWorking(daoActions[1], oneRoleId2, null, null, null, results[5], results[5]);
292 			assertRoleMemberListCachingIsWorking(daoActions[2], roleIds, null, null, null, results[6], allResults[2]);
293 			assertRoleMemberListCachingIsWorking(daoActions[2], oneRoleId, null, null, null, results[6], results[6]);
294 			assertRoleMemberListCachingIsWorking(daoActions[2], oneRoleId2, null, null, null, new String[]{}, new String[]{});
295 			assertRoleMemberListCachingIsWorking(daoActions[2], oneRoleId4, null, null, null, new String[]{}, allResults[3]);
296 			assertRoleMemberListCachingIsWorking(daoActions[3], roleIds, null, null, Role.PRINCIPAL_MEMBER_TYPE, results[2], allResults[1]);
297 			assertRoleMemberListCachingIsWorking(daoActions[3], roleIds, null, null, Role.GROUP_MEMBER_TYPE, results[3], results[3]);
298 			assertRoleMemberListCachingIsWorking(daoActions[3], roleIds, null, null, Role.ROLE_MEMBER_TYPE, results[6], allResults[2]);
299 			assertRoleMemberListCachingIsWorking(daoActions[3], roleIds, null, null, null, cachedRoleMemberIdsArray, allRoleMemberIdsArray);
300 			assertRoleMemberListCachingIsWorking(daoActions[4], roleIds, null, groupIds, null, cachedRoleMemberIdsArray, allRoleMemberIdsArray);
301 			assertRoleMemberListCachingIsWorking(daoActions[4], roleIds, "p10", oneGroupId, null, results[7], allResults[4]);
302 			assertRoleMemberListCachingIsWorking(daoActions[4], roleIds, "p10", null, null, results[8], allResults[5]);
303 			assertRoleMemberListCachingIsWorking(daoActions[4], oneRoleId2, "p5", groupIds, null, results[9], results[9]);
304 		}
305 		
306 		/*
307 		 * Tests the searches on the KimDelegationImpl and KimDelegationMemberImpl caches.
308 		 */
309 		private void assertDelegationCachingWorksAsExpected() throws Exception {
310 			List<String> roleIds1 = Arrays.asList(new String[] {"r3", "r4", "r5"});
311 			List<String> roleIds2 = Collections.singletonList("r5");
312 			List<String> roleIds3 = Collections.singletonList("r4");
313 			List<String> delegationIds = Arrays.asList(new String[] {"d1", "d2"});
314 			List<String> delegationMemberIds = Arrays.asList(new String[] {"d1p7", "d1r4", "d2g7"});
315 			
316 			// Ensure that by-ID caching of individual delegations is working properly.
317 			assertKimObjectCachingByIdIsWorking(delegationIds, delegationIds, KIM_DELEGATION_IMPL_CHECKER);
318 			
319 			// Ensure that lists of delegations can be cached properly.
320 			assertDelegationListCachingIsWorking(roleIds1, delegationIds, true);
321 			assertDelegationListCachingIsWorking(roleIds1, delegationIds, false);
322 			assertDelegationListCachingIsWorking(roleIds2, delegationIds, true);
323 			assertDelegationListCachingIsWorking(roleIds2, delegationIds, false);
324 			assertDelegationListCachingIsWorking(roleIds3, new ArrayList<String>(), true);
325 			assertDelegationListCachingIsWorking(roleIds3, new ArrayList<String>(), false);
326 			assertDelegationListCachingIsWorking(null, new ArrayList<String>(), true);
327 			assertDelegationListCachingIsWorking(null, new ArrayList<String>(), false);
328 			
329 			// -----------------------------------------------------------------------------------
330 			
331 			List<String> oneDelegationId1 = Collections.singletonList("d1");
332 			List<String> oneDelegationId2 = Collections.singletonList("d2");
333 			List<String> groupIds = Arrays.asList(new String[] {"g6", "g7"});
334 			List<String> oneGroupId1 = Collections.singletonList("g7");
335 			List<String> oneGroupId2 = Collections.singletonList("g6");
336 			String[][] results = {{}, {"d1p7"}, {"d2g7"}, {"d1p7","d1r4"}, {"d1p7","d1r4","d2g7"}};
337 			RoleDaoAction delegationPrincipals = RoleDaoAction.DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS;
338 			RoleDaoAction delegationGroups = RoleDaoAction.DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS;
339 			RoleDaoAction delegationMembers = RoleDaoAction.DELEGATION_MEMBERS_FOR_DELEGATION_IDS;
340 			
341 			// Ensure that by-ID caching of individual delegation members is working properly.
342 			assertKimObjectCachingByIdIsWorking(delegationMemberIds, delegationMemberIds, KIM_DLGN_MBR_IMPL_CHECKER);
343 			
344 			// Ensure that by-ID, by-delegation caching of individual delegation members is working properly.
345 			String[] dlgnMemberIdArray = {"d1p7","d1r4","d2g7"};
346 			String[][] idArray = { {"d1","d1p7"}, {"d1","d1r4"}, {"d2","d2g7"} };
347 			Map<String,DelegateMemberBo> firstMemberMap = getKimDelegationMemberImplMapByIdAndDelegationId(idArray);
348 			for (String dlgnMemberId : dlgnMemberIdArray) {
349 				assertNotNull("The Map of members by delegation member ID and delegation ID should have contained an entry for ID " + dlgnMemberId,
350 						firstMemberMap.get(dlgnMemberId));
351 			}
352 			assertDelegationMembersAreCachedByIdAndDelegationIdAsExpected(idArray, true);
353 			Map<String,DelegateMemberBo> secondMemberMap = getKimDelegationMemberImplMapByIdAndDelegationId(idArray);
354 			assertKimObjectResultsAreEqual(idArray.length, firstMemberMap, secondMemberMap, KIM_DLGN_MBR_IMPL_CHECKER);
355 			// Ensure that delegation members cached by ID and delegation ID can be cleared properly.
356 			getIdentityManagementNotificationService().delegationUpdated();
357 			assertDelegationMembersAreCachedByIdAndDelegationIdAsExpected(idArray, false);
358 			firstMemberMap = getKimDelegationMemberImplMapByIdAndDelegationId(idArray);
359 			assertKimObjectResultsAreEqual(idArray.length, secondMemberMap, firstMemberMap, KIM_DLGN_MBR_IMPL_CHECKER);
360 			// Clean up the cache.
361 			getIdentityManagementNotificationService().roleUpdated();
362 			
363 			// Ensure that by-member-ID, by-delegation-ID caching of delegation member Lists is working properly.
364 			String[][] idArray2 = { {"p7","d1","d1p7"}, {"r4","d1","d1r4"}, {"g7","d2","d2g7"}};
365 			firstMemberMap = getKimDelegationMemberImplMapByMemberAndDelegationId(idArray2);
366 			for (String dlgnMemberId : dlgnMemberIdArray) {
367 				assertNotNull("The Map of members by member ID and delegation ID should have contained an entry for ID " + dlgnMemberId,
368 						firstMemberMap.get(dlgnMemberId));
369 			}
370 			assertDelegationMemberListsAreCachedByMemberAndDelegationIdAsExpected(idArray2, true);
371 			secondMemberMap = getKimDelegationMemberImplMapByMemberAndDelegationId(idArray2);
372 			assertKimObjectResultsAreEqual(idArray.length, firstMemberMap, secondMemberMap, KIM_DLGN_MBR_IMPL_CHECKER);
373 			// Ensure that delegation member Lists cached by member ID and delegation ID can be cleared properly.
374 			getIdentityManagementNotificationService().delegationUpdated();
375 			assertDelegationMemberListsAreCachedByMemberAndDelegationIdAsExpected(idArray2, false);
376 			firstMemberMap = getKimDelegationMemberImplMapByMemberAndDelegationId(idArray2);
377 			assertKimObjectResultsAreEqual(idArray.length, secondMemberMap, firstMemberMap, KIM_DLGN_MBR_IMPL_CHECKER);
378 			// Clean up the cache.
379 			getIdentityManagementNotificationService().roleUpdated();
380 			
381 			// Ensure that lists of delegation members can be cached properly.
382 			assertDelegationMemberListCachingIsWorking(delegationPrincipals, delegationIds, "p7", null, results[1]);
383 			assertDelegationMemberListCachingIsWorking(delegationPrincipals, oneDelegationId1, "p7", null, results[1]);
384 			assertDelegationMemberListCachingIsWorking(delegationPrincipals, oneDelegationId2, "p7", null, results[0]);
385 			assertDelegationMemberListCachingIsWorking(delegationPrincipals, delegationIds, "p10", null, results[0]);
386 			assertDelegationMemberListCachingIsWorking(delegationPrincipals, null, "p7", null, results[1]);
387 			assertDelegationMemberListCachingIsWorking(delegationPrincipals, delegationIds, null, null, results[1]);
388 			assertDelegationMemberListCachingIsWorking(delegationGroups, delegationIds, null, groupIds, results[2]);
389 			assertDelegationMemberListCachingIsWorking(delegationGroups, oneDelegationId2, null, groupIds, results[2]);
390 			assertDelegationMemberListCachingIsWorking(delegationGroups, oneDelegationId1, null, groupIds, results[0]);
391 			assertDelegationMemberListCachingIsWorking(delegationGroups, delegationIds, null, oneGroupId1, results[2]);
392 			assertDelegationMemberListCachingIsWorking(delegationGroups, delegationIds, null, oneGroupId2, results[0]);
393 			assertDelegationMemberListCachingIsWorking(delegationGroups, null, null, groupIds, results[2]);
394 			assertDelegationMemberListCachingIsWorking(delegationGroups, delegationIds, null, null, results[2]);
395 			assertDelegationMemberListCachingIsWorking(delegationMembers, oneDelegationId1, null, null, results[3]);
396 			assertDelegationMemberListCachingIsWorking(delegationMembers, oneDelegationId2, null, null, results[2]);
397 			assertDelegationMemberListCachingIsWorking(delegationMembers, delegationIds, null, null, results[4]);
398 			assertDelegationMemberListCachingIsWorking(delegationMembers, null, null, null, results[4]);
399 		}
400 		
401 		// --------------------------------------------------------------------------------------------------------------------
402 		// Methods related to testing the clearing of the cache upon updates to various objects.
403 		// --------------------------------------------------------------------------------------------------------------------
404 		
405 		/*
406 		 * Tests various update and deletion operations to ensure that the correct caches are cleared as a result of the modifications.
407 		 * Note that after updating/deleting role members or delegation members, this method needs to refresh the member list on the
408 		 * associated role/delegation manually in order for the deletion testing to work as expected, at least for OJB.
409 		 */
410 		private void assertCachesAreClearedOnUpdatesAsExpected() throws Exception {
411 			Map<String, String> map = new HashMap<String, String>();
412 			Timestamp yesterday = new Timestamp( System.currentTimeMillis() - (24*60*60*1000) );
413 			// Some sample roles, role members, delegations, and delegation members to use for testing the statuses of the caches.
414 			List<String> roleIds = Arrays.asList(new String[] {"r4", "r5"});
415 			List<String> roleMemberIds = Arrays.asList(new String[] {"r4p10", "r4g5"});
416 			List<String> delegationIds = Arrays.asList(new String[] {"d1", "d2"});
417 			List<String> delegationMemberIds = Arrays.asList(new String[] {"d1p7","d1r4"});
418 			// The primary keys to use for object insertion/deletion.
419 			List<String> oneRoleId = Collections.singletonList("r3");
420 			List<String> oneGroupId = Collections.singletonList("g8");
421 			List<String> oneGroupId2 = Collections.singletonList("g7");
422 			List<String> oneRoleIdAsMember = Collections.singletonList("r6");
423 			List<String> oneDelegationId = null;
424 			String principalRoleMemberId = null;
425 			String groupRoleMemberId = null;
426 			String roleAsMemberRoleMemberId = null;
427 			String delegationId = null;
428 			String principalDelegationMemberId = null;
429 			String groupDelegationMemberId = null;
430 			
431 			// Ensure that role-assigning tasks will reset the correct caches.
432 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
433 			roleService.assignPrincipalToRole("p8", "AUTH_SVC_TEST2", "RoleThree", map);
434 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
435 			getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, "r3").refreshReferenceObject("members");
436 			principalRoleMemberId = assertRoleMemberUpdateSucceeded("p8", getStoredRolePrincipalsForPrincipalIdAndRoleIds(oneRoleId, "p8",
437                     map), true);
438 			
439 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
440 			roleService.assignGroupToRole("g8", "AUTH_SVC_TEST2", "RoleThree", map);
441 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
442 			getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, "r3").refreshReferenceObject("members");
443 			groupRoleMemberId = assertRoleMemberUpdateSucceeded("g8", getStoredRoleGroupsForGroupIdsAndRoleIds(oneRoleId, oneGroupId, null), true);
444 			
445 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
446 			roleService.assignRoleToRole("r6", "AUTH_SVC_TEST2", "RoleThree", map);
447 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
448 			getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, "r3").refreshReferenceObject("members");
449 			roleAsMemberRoleMemberId = assertRoleMemberUpdateSucceeded("r6", getStoredRoleMembershipsForRoleIdsAsMembers(oneRoleIdAsMember,
450                     map), true);
451 			
452 			// Ensure that deletion tasks will reset the correct caches.
453 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
454 			roleService.removePrincipalFromRole("p8", "AUTH_SVC_TEST2", "RoleThree", map);
455 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
456 			getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, "r3").refreshReferenceObject("members");
457 			assertRoleMemberUpdateSucceeded("p8", getStoredRolePrincipalsForPrincipalIdAndRoleIds(oneRoleId, "p8",
458                     map), false);
459 			assertRoleMemberHasExpectedExistence(principalRoleMemberId, false);
460 			
461 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
462 			roleService.removeGroupFromRole("g8", "AUTH_SVC_TEST2", "RoleThree", map);
463 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
464 			getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, "r3").refreshReferenceObject("members");
465 			assertRoleMemberUpdateSucceeded("g8", getStoredRoleGroupsForGroupIdsAndRoleIds(oneRoleId, oneGroupId, null), false);
466 			assertRoleMemberHasExpectedExistence(groupRoleMemberId, false);
467 			
468 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
469 			roleService.removeRoleFromRole("r6", "AUTH_SVC_TEST2", "RoleThree", map);
470 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
471 			getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, "r3").refreshReferenceObject("members");
472 			assertRoleMemberUpdateSucceeded("r6", getStoredRoleMembershipsForRoleIdsAsMembers(oneRoleIdAsMember,
473                     map), false);
474 			assertRoleMemberHasExpectedExistence(roleAsMemberRoleMemberId, false);
475 			
476 			// This time, do role-assigning tasks via the "saveRoleMemberForRole" method.
477 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
478 			roleService.saveRoleMemberForRole(null, "p8", "P", "r3", map, null, null);
479 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
480 			getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, "r3").refreshReferenceObject("members");
481 			principalRoleMemberId = assertRoleMemberUpdateSucceeded("p8", getStoredRolePrincipalsForPrincipalIdAndRoleIds(oneRoleId, "p8",
482                     map), true);
483 			
484 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
485 			roleService.saveRoleMemberForRole(null, "g8", "G", "r3", map, null, null);
486 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
487 			getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, "r3").refreshReferenceObject("members");
488 			groupRoleMemberId = assertRoleMemberUpdateSucceeded("g8", getStoredRoleGroupsForGroupIdsAndRoleIds(oneRoleId, oneGroupId, null), true);
489 			
490 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
491 			roleService.saveRoleMemberForRole(null, "r6", "R", "r3", map, null, null);
492 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
493 			getBusinessObjectService().findBySinglePrimaryKey(RoleBo.class, "r3").refreshReferenceObject("members");
494 			roleAsMemberRoleMemberId = assertRoleMemberUpdateSucceeded("r6", getStoredRoleMembershipsForRoleIdsAsMembers(oneRoleIdAsMember,
495                     map), true);
496 			
497 			// Now perform some delegation-assigning tasks, which currently clear the role-related caches in addition to the delegation-related caches.
498 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
499 			roleService.saveDelegationMemberForRole(null, principalRoleMemberId, "p6", "P", "P", "r3", map, null, null);
500 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
501 			
502 			List<DelegateBo> newDelegation = this.getStoredDelegationImplsForRoleIds(oneRoleId);
503 			assertEquals("The wrong number of new delegations were found for role r3.", 1, newDelegation.size());
504 			delegationId = newDelegation.get(0).getDelegationId();
505 			oneDelegationId = Collections.singletonList(delegationId);
506 			
507 			getBusinessObjectService().findBySinglePrimaryKey(DelegateBo.class, delegationId).refreshReferenceObject("members");
508 			principalDelegationMemberId =
509 				assertDelegationMemberUpdateSucceeded("p6", this.getStoredDelegationPrincipalsForPrincipalIdAndDelegationIds(oneDelegationId, "p6"), true);
510 			
511 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
512 			roleService.saveDelegationMemberForRole(null, principalRoleMemberId, "g7", "G", "P", "r3", map, null, null);
513 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
514 			getBusinessObjectService().findBySinglePrimaryKey(DelegateBo.class, delegationId).refreshReferenceObject("members");
515 			groupDelegationMemberId =
516 				assertDelegationMemberUpdateSucceeded("g7", this.getStoredDelegationGroupsForGroupIdsAndDelegationIds(oneDelegationId, oneGroupId2), true);
517 			
518 			// Ensure that inactivation of principal role/delegation members and group role members works properly.
519 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
520 			inactivatePrincipalRoleMemberships("p8", yesterday);
521 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
522 			getBusinessObjectService().findBySinglePrimaryKey(DelegateBo.class, "r3").refreshReferenceObject("members");
523 			assertRoleMemberUpdateSucceeded("p8", getStoredRolePrincipalsForPrincipalIdAndRoleIds(oneRoleId, "p8", null), false);
524 			assertRoleMemberHasExpectedExistence(principalRoleMemberId, true);
525 			
526 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
527 			inactivateGroupRoleMemberships(oneGroupId, yesterday);
528 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
529 			getBusinessObjectService().findBySinglePrimaryKey(DelegateBo.class, "r3").refreshReferenceObject("members");
530 			assertRoleMemberUpdateSucceeded("g8", getStoredRoleGroupsForGroupIdsAndRoleIds(oneRoleId, oneGroupId, null), false);
531 			assertRoleMemberHasExpectedExistence(groupRoleMemberId, true);
532 			
533 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
534 			inactivatePrincipalDelegations("p6", yesterday);
535 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, true);
536 			getBusinessObjectService().findBySinglePrimaryKey(DelegateBo.class, delegationId).refreshReferenceObject("members");
537 			assertDelegationMemberUpdateSucceeded("p6", this.getStoredDelegationPrincipalsForPrincipalIdAndDelegationIds(oneDelegationId, "p6"), false);
538 			assertDelegationMemberHasExpectedExistence(principalDelegationMemberId, true);
539 			
540 			// Ensure that general role inactivation (including its delegations and memberships) works properly.
541 			// Note that the code below will inactivate the roles "r6" and "r3".
542 			
543 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
544 			roleInactivated("r6");
545 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
546 			getBusinessObjectService().findBySinglePrimaryKey(DelegateBo.class, "r3").refreshReferenceObject("members");
547 			assertRoleMemberUpdateSucceeded("r6", getStoredRoleMembershipsForRoleIdsAsMembers(oneRoleIdAsMember, null), false);
548 			assertRoleMemberHasExpectedExistence(roleAsMemberRoleMemberId, true);
549 			
550 			assertAndPopulateCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds);
551 			roleInactivated("r3");
552 			assertCorrectObjectsWereClearedFromCache(roleIds, roleMemberIds, delegationIds, delegationMemberIds, false);
553 			getBusinessObjectService().findBySinglePrimaryKey(DelegateBo.class, delegationId).refreshReferenceObject("members");
554 			assertDelegationMemberUpdateSucceeded("g7", this.getStoredDelegationGroupsForGroupIdsAndDelegationIds(oneDelegationId, oneGroupId2), false);
555 			assertDelegationMemberHasExpectedExistence(groupDelegationMemberId, true);
556 			
557 			// Clean up the cache when done.
558 			getIdentityManagementNotificationService().roleUpdated();
559 		}
560 		
561 		/*
562 		 * Test to verify a refresh period works and caches are cleared.
563 		 */
564 		private void assertCachesAreRefreshedAsExpected() throws Exception {
565 			List<String> roleIds = Arrays.asList(new String[] {"r3"});
566 			String[][] roleNames = {{"AUTH_SVC_TEST2","RoleThree"}};
567 			
568 			// Ensure that by-ID caching of individual roles is working properly.
569 			assertKimObjectCachingByIdIsWorking(roleIds, roleIds, ROLE_IMPL_CHECKER);
570 			
571 			// Ensure that roles can be obtained properly from the cache by name.
572 			Map<String,RoleBo> firstRoleMap = getRoleImplMapByName(roleNames);
573 			Map<String,RoleBo> secondRoleMap = getRoleImplMapByName(roleNames);
574 			assertKimObjectResultsAreEqual(roleIds.size(), firstRoleMap, secondRoleMap, ROLE_IMPL_CHECKER);
575 
576 			assertRolesAreCachedByNameAsExpected(roleNames, true);
577 
578 			// Sleep for 12 seconds, because roles should only be cached for 10 seconds in this situation.
579 			Thread.sleep(12000);
580 			
581 			assertRolesAreCachedByNameAsExpected(roleNames, false);
582 			
583 			// Clean up the cache.
584 			getIdentityManagementNotificationService().roleUpdated();
585 		}
586 		
587 		/*
588 		 * A convenience method for populating the cache and asserting that it has been populated correctly.
589 		 */
590 		private void assertAndPopulateCache(List<String> roleIds, List<String> roleMbrIds, List<String> dlgnIds, List<String> dlgnMbrIds) throws Exception {
591 			getKimObjectMapById(roleIds, ROLE_IMPL_CHECKER);
592 			getKimObjectMapById(roleMbrIds, ROLE_MEMBER_IMPL_CHECKER);
593 			getKimObjectMapById(dlgnIds, KIM_DELEGATION_IMPL_CHECKER);
594 			getKimObjectMapById(dlgnMbrIds, KIM_DLGN_MBR_IMPL_CHECKER);
595 			assertKimObjectsAreCachedByIdAsExpected(roleIds, true, ROLE_IMPL_CHECKER);
596 			assertKimObjectsAreCachedByIdAsExpected(roleMbrIds, true, ROLE_MEMBER_IMPL_CHECKER);
597 			assertKimObjectsAreCachedByIdAsExpected(dlgnIds, true, KIM_DELEGATION_IMPL_CHECKER);
598 			assertKimObjectsAreCachedByIdAsExpected(dlgnMbrIds, true, KIM_DLGN_MBR_IMPL_CHECKER);
599 		}
600 		
601 		/*
602 		 * A convenience method for ensuring that the correct objects have been cleared from the cache.
603 		 */
604 		private void assertCorrectObjectsWereClearedFromCache(List<String> roleIds, List<String> roleMbrIds, List<String> dlgnIds,
605 				List<String> dlgnMbrIds, boolean onlyClearedDelegationCache) throws Exception {
606 			assertKimObjectsAreCachedByIdAsExpected(roleIds, onlyClearedDelegationCache, ROLE_IMPL_CHECKER);
607 			assertKimObjectsAreCachedByIdAsExpected(roleMbrIds, onlyClearedDelegationCache, ROLE_MEMBER_IMPL_CHECKER);
608 			assertKimObjectsAreCachedByIdAsExpected(dlgnIds, false, KIM_DELEGATION_IMPL_CHECKER);
609 			assertKimObjectsAreCachedByIdAsExpected(dlgnMbrIds, false, KIM_DLGN_MBR_IMPL_CHECKER);
610 		}
611 		
612 		/*
613 		 * A convenience method for ensuring that the given role member has been added to or removed from the given role.
614 		 * It is assumed that memberList was obtained via a searching mechanism that automatically excludes inactive role members, so even if
615 		 * memberList is empty, that does not necessarily mean the member was deleted; use assertRoleMemberHasExpectedExistence() to find inactive members.
616 		 * If the assertions succeed, this method will return null if shouldBeInList is false, or the role member's ID if shouldBeInList is true.
617 		 */
618 		private String assertRoleMemberUpdateSucceeded(String memberId, List<RoleMemberBo> memberList, boolean shouldBeInList) {
619 			if (shouldBeInList) {
620 				assertEquals("The role member list has the wrong number of members.", 1, memberList.size());
621 				assertEquals("The role member does not have the expected member ID", memberId, memberList.get(0).getMemberId());
622 				return memberList.get(0).getRoleMemberId();
623 			} else {
624 				assertEquals("The role member list has the wrong number of members.", 0, memberList.size());
625 				return null;
626 			}
627 		}
628 		
629 		/*
630 		 * A convenience method for checking whether or not a given role member is truly deleted or is just inactive.
631 		 */
632 		private void assertRoleMemberHasExpectedExistence(String roleMemberId, boolean justInactive) {
633 			RoleMemberBo roleMember = getRoleMemberBo(roleMemberId);
634 			if (justInactive) {
635 				assertNotNull("There should be an inactive but existing role member with ID " + roleMemberId, roleMember);
636 				assertFalse(roleMemberId + " should be an inactive role member", roleMember.isActive(new Timestamp(System.currentTimeMillis())));
637 			} else {
638 				assertNull("There should not be an existing role member with ID " + roleMemberId, roleMember);
639 			}
640 		}
641 		
642 		/*
643 		 * A convenience method for ensuring that the given delegation member has been added to or removed from the given delegation.
644 		 * It is assumed that memberList was obtained via a searching mechanism that automatically excludes inactive delegation members, so even if
645 		 * memberList is empty, that does not always mean the member was deleted; use assertDelegationMemberHasExpectedExistence() to find inactive members.
646 		 * If the assertions succeed, this method will return null if shouldBeInList is false, or the delegation member's ID if shouldBeInList is true.
647 		 */
648 		private String assertDelegationMemberUpdateSucceeded(String memberId, List<DelegateMemberBo> memberList, boolean shouldBeInList) {
649 			if (shouldBeInList) {
650 				assertEquals("The delegation member list has the wrong number of members.", 1, memberList.size());
651 				assertEquals("The delegation member does not have the expected member ID", memberId, memberList.get(0).getMemberId());
652 				return memberList.get(0).getDelegationMemberId();
653 			} else {
654 				assertEquals("The delegation member list has the wrong number of members.", 0, memberList.size());
655 				return null;
656 			}
657 		}
658 		
659 		/*
660 		 * A convenience method for checking whether or not a given delegation member is truly deleted or is just inactive.
661 		 */
662 		private void assertDelegationMemberHasExpectedExistence(String delegationMemberId, boolean justInactive) {
663 			DelegateMemberBo delegationMember = getDelegateMemberBo(delegationMemberId);
664 			if (justInactive) {
665 				assertNotNull("There should be an inactive but existing delegation member with ID " + delegationMemberId, delegationMember);
666 				assertFalse(delegationMemberId + " should be an inactive delegation member", delegationMember.isActive(new Timestamp(System.currentTimeMillis())));
667 			} else {
668 				assertNull("There should not be an existing delegation member with ID " + delegationMemberId, delegationMember);
669 			}
670 		}
671 		
672 		// --------------------------------------------------------------------------------------------------------------------
673 		// Convenience methods related to RoleImpl, RoleMemberImpl, KimDelegationImpl, and KimDelegationMemberImpl caching.
674 		// --------------------------------------------------------------------------------------------------------------------
675 		
676 		/*
677 		 * A convenience method for obtaining KIM object Maps based on a list of IDs.
678 		 */
679 		private <T extends PersistableBusinessObjectBase> Map<String,T> getKimObjectMapById(
680 				List<String> kimObjectIds, KimObjectTestChecker<T> objectTestChecker) {
681 			Map<String,T> kimObjectMap = new HashMap<String,T>();
682 			for (String kimObjectId : kimObjectIds) {
683 				T tempKimObject = objectTestChecker.getKimObjectById(kimObjectId);
684 				if (tempKimObject != null) {
685 					kimObjectMap.put(kimObjectId, tempKimObject);
686 				}
687 			}
688 			return kimObjectMap;
689 		}
690 
691 		/*
692 		 * A convenience method for converting a List of KIM objects into a Map containing mappings between IDs and objects.
693 		 */
694 		private <T extends PersistableBusinessObjectBase> Map<String,T> convertKimObjectListToMap(List<T> kimObjectList,
695 				KimObjectTestChecker<T> objectTestChecker) {
696 			Map<String,T> kimObjectMap = new HashMap<String,T>();
697 			if (kimObjectList != null && !kimObjectList.isEmpty()) {
698 				for (T tempKimObject : kimObjectList) {
699 					kimObjectMap.put(objectTestChecker.getKimObjectId(tempKimObject), tempKimObject);
700 				}
701 			}
702 			return kimObjectMap;
703 		}
704 		
705 		/*
706 		 * Checks to ensure that two Maps have the expected size and whose corresponding KIM objects have certain identical fields.
707 		 */
708 		private <T extends PersistableBusinessObjectBase> void assertKimObjectResultsAreEqual(int objectCount, Map<String,T> oldKimObjects,
709 				Map<String,T> newKimObjects, KimObjectTestChecker<T> objectTestChecker) throws Exception {
710 			final String objectName = objectTestChecker.getKimObjectName();
711 			assertEquals("Original " + objectName + " map has the wrong number of elements", objectCount, oldKimObjects.size());
712 			assertEquals("New " + objectName + " map has the wrong number of elements", objectCount, newKimObjects.size());
713 			for (Map.Entry<String,T> originalKimObject : oldKimObjects.entrySet()) {
714 				T oldKimObject = originalKimObject.getValue();
715 				T newKimObject = newKimObjects.get(originalKimObject.getKey());
716 				assertNotNull("Old " + objectName + " cannot be null", oldKimObject);
717 				assertNotNull("New " + objectName + " cannot be null", newKimObject);
718 				if (oldKimObject != newKimObject) {
719 					objectTestChecker.assertKimObjectsAreEqual(oldKimObject, newKimObject);
720 				}
721 			}
722 		}
723 		
724 		/*
725 		 * A convenience method for ensuring that the by-ID caching for individual KIM objects is working properly.
726 		 */
727 		private <T extends PersistableBusinessObjectBase> void assertKimObjectCachingByIdIsWorking(
728 				List<String> cachedKimObjectIds, List<String> allKimObjectIds, KimObjectTestChecker<T> objectTestChecker) throws Exception {
729 			// Ensure that KIM objects can be obtained properly from the cache by ID.
730 			Map<String,T> firstKimObjectMap = getKimObjectMapById(allKimObjectIds, objectTestChecker);
731 			for (String kimObjectId : allKimObjectIds) {
732 				assertNotNull("The results should have included the item with ID " + kimObjectId, firstKimObjectMap.get(kimObjectId));
733 			}
734 			assertKimObjectsAreCachedByIdAsExpected(cachedKimObjectIds, true, objectTestChecker);
735 			Map<String,T> secondKimObjectMap = getKimObjectMapById(allKimObjectIds, objectTestChecker);
736 			assertKimObjectResultsAreEqual(allKimObjectIds.size(), firstKimObjectMap, secondKimObjectMap, objectTestChecker);
737 			
738 			// Ensure that KIM objects cached by ID can be cleared properly.
739 			getIdentityManagementNotificationService().roleUpdated();
740 			assertKimObjectsAreCachedByIdAsExpected(cachedKimObjectIds, false, objectTestChecker);
741 			firstKimObjectMap = getKimObjectMapById(allKimObjectIds, objectTestChecker);
742 			assertKimObjectResultsAreEqual(allKimObjectIds.size(), secondKimObjectMap, firstKimObjectMap, objectTestChecker);
743 			
744 			// Ensure that clearing the delegation cache has the desired effect (or lack of effect) on the KIM object's cache.
745 			getIdentityManagementNotificationService().delegationUpdated();
746 			assertKimObjectsAreCachedByIdAsExpected(cachedKimObjectIds, objectTestChecker.isUnaffectedByClearingDelegationCache(), objectTestChecker);
747 			secondKimObjectMap = getKimObjectMapById(allKimObjectIds, objectTestChecker);
748 			assertKimObjectResultsAreEqual(allKimObjectIds.size(), firstKimObjectMap, secondKimObjectMap, objectTestChecker);
749 			
750 			// Clean up the cache when done.
751 			getIdentityManagementNotificationService().roleUpdated();
752 		}
753 		
754 		/*
755 		 * A convenience method for checking whether or not certain KIM objects are in their by-ID caches.
756 		 */
757 		private void assertKimObjectsAreCachedByIdAsExpected(List<String> kimObjectIds, boolean shouldBeInCache,
758 				KimObjectTestChecker<? extends PersistableBusinessObjectBase> objectTestChecker) {
759 			for (String kimObjectId : kimObjectIds) {
760 				PersistableBusinessObjectBase tempKimObject = objectTestChecker.getKimObjectFromCacheById(kimObjectId);
761 				if (shouldBeInCache) {
762 					if (tempKimObject == null) {
763 						assertNotNull(objectTestChecker.getKimObjectName() + " with ID '" + kimObjectId + "' should have been in the cache",tempKimObject);
764 					}
765 				} else if (tempKimObject != null) {
766 					assertNull(objectTestChecker.getKimObjectName() + " with ID '" + kimObjectId + "' should not have been in the cache",tempKimObject);
767 				}
768 			}
769 		}
770 		
771 		// --------------------------------------------------------------------------------------------------------------------
772 		// Convenience methods related to RoleImpl caching.
773 		// --------------------------------------------------------------------------------------------------------------------
774 		
775 		/*
776 		 * A convenience method for retrieving multiple RoleImpl objects by namespace code and name.
777 		 */
778 		private Map<String,RoleBo> getRoleImplMapByName(String[][] roleNames) {
779 			Map<String,RoleBo> roleMap = new HashMap<String,RoleBo>();
780 			for (int i = 0; i < roleNames.length; i++) {
781 				RoleBo tempRole = getRoleBoByName(roleNames[i][0], roleNames[i][1]);
782 				if (tempRole != null && tempRole.isActive()) {
783 					roleMap.put(tempRole.getId(), tempRole);
784 				}
785 			}
786 			return roleMap;
787 		}
788 		
789 		/*
790 		 * A convenience method for checking whether or not certain roles are in the by-name cache.
791 		 */
792 		private void assertRolesAreCachedByNameAsExpected(String[][] roleNames, boolean shouldBeInCache) {
793 			for (int i = 0; i < roleNames.length; i++) {
794 				RoleBo tempRole = getRoleFromCache(roleNames[i][0], roleNames[i][1]);
795 				if (shouldBeInCache) {
796 					if (tempRole == null) {
797 						assertNotNull("Role with namespace '" + roleNames[i][0] + "' and name '" + roleNames[i][1] +
798 								"' should have been in the cache", tempRole);
799 					}
800 				} else if (tempRole != null) {
801 					assertNull("Role with namespace '" + roleNames[i][0] + "' and name '" + roleNames[i][1] +
802 							"' should not have been in the cache", tempRole);
803 				}
804 			}
805 		}
806 		
807 		// --------------------------------------------------------------------------------------------------------------------
808 		// Convenience methods related to RoleMemberImpl caching.
809 		// --------------------------------------------------------------------------------------------------------------------
810 		
811 		/*
812 		 * A convenience method for choosing the correct helper method that accesses "getRoleMemberImplList".
813 		 */
814 		private List<RoleMemberBo> getCorrectRoleMemberImplList(RoleDaoAction daoAction, List<String> roleIds, String principalId,
815 				List<String> groupIds, String memberTypeCode) {
816 			switch (daoAction) {
817 				case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS : return getStoredRolePrincipalsForPrincipalIdAndRoleIds(roleIds, principalId, null);
818 				case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS : return getStoredRoleGroupsForGroupIdsAndRoleIds(roleIds, groupIds, null);
819 				case ROLE_MEMBERS_FOR_ROLE_IDS : return getStoredRoleMembersForRoleIds(roleIds, memberTypeCode, null);
820 				case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS : return getStoredRoleMembershipsForRoleIdsAsMembers(roleIds, null);
821 				case ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS : return getStoredRoleMembersForRoleIdsWithFilters(roleIds, principalId, groupIds, null);
822 				default : throw new IllegalArgumentException("The 'daoAction' parameter cannot be a non-role-member-related value!");
823 			}
824 		}
825 		
826 		/*
827 		 * A convenience method for checking whether or not a certain RoleMemberImpl List should be allowed in the cache. If we are using the KimRoleDao's
828 		 * "getRoleMembershipsForRoleIdsAsMembers" method, then the list obtained via the given ID should be allowed in the cache if its members belong
829 		 * to roles that permit member caching. Otherwise, the list should be allowed in the cache if shouldCacheMembersOfRole(roleId) is true.
830 		 */
831 		private boolean listShouldBeAllowedInCache(RoleDaoAction daoAction, String roleId) {
832 			if (RoleDaoAction.ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS.equals(daoAction)) {
833 				List<RoleMemberBo> roleMembers = getRoleDao().getRoleMembershipsForRoleIdsAsMembers(Collections.singletonList(roleId), null);
834 				for (RoleMemberBo roleMember : roleMembers) {
835 					if (!shouldCacheMembersOfRole(roleMember.getRoleId())) {
836 						return false;
837 					}
838 				}
839 				return true;
840 			} else {
841 				return shouldCacheMembersOfRole(roleId);
842 			}
843 		}
844 		
845 		/*
846 		 * A convenience method for checking whether or not certain RoleMemberImpl Lists are being cached.
847 		 */
848 		private void assertRoleMemberListCachingIsWorking(RoleDaoAction daoAction, List<String> roleIds, String principalId,
849 				List<String> groupIds, String memberTypeCode, String[] expectedCachedMembersArray, String[] expectedMembersArray) throws Exception {
850 			List<String> expectedCachedMembers = Arrays.asList(expectedCachedMembersArray);
851 			List<String> expectedMembers = Arrays.asList(expectedMembersArray);
852 			// Ensure that the RoleMemberImpl lists are getting cached as expected.
853 			Map<String,RoleMemberBo> firstMemberMap = convertKimObjectListToMap(
854 					getCorrectRoleMemberImplList(daoAction, roleIds, principalId, groupIds, memberTypeCode), ROLE_MEMBER_IMPL_CHECKER);
855 			for (String expectedMember : expectedMembers) {
856 				assertNotNull("The retrieved role members should have included the member with ID " + expectedMember, firstMemberMap.get(expectedMember));
857 			}
858 			assertRoleMemberListsAreCachedAsExpected(daoAction, roleIds, principalId, groupIds, memberTypeCode, expectedCachedMembers, true);
859 			Map<String,RoleMemberBo> secondMemberMap = convertKimObjectListToMap(
860 					getCorrectRoleMemberImplList(daoAction, roleIds, principalId, groupIds, memberTypeCode), ROLE_MEMBER_IMPL_CHECKER);
861 			assertKimObjectResultsAreEqual(expectedMembers.size(), firstMemberMap, secondMemberMap, ROLE_MEMBER_IMPL_CHECKER);
862 			
863 			// Ensure that the RoleMemberImpl lists can be cleared from the cache properly.
864 			getIdentityManagementNotificationService().roleUpdated();
865 			assertRoleMemberListsAreCachedAsExpected(daoAction, roleIds, principalId, groupIds, memberTypeCode, expectedCachedMembers, false);
866 			firstMemberMap = convertKimObjectListToMap(
867 					getCorrectRoleMemberImplList(daoAction, roleIds, principalId, groupIds, memberTypeCode), ROLE_MEMBER_IMPL_CHECKER);
868 			assertKimObjectResultsAreEqual(expectedMembers.size(), secondMemberMap, firstMemberMap, ROLE_MEMBER_IMPL_CHECKER);
869 			
870 			// Ensure that clearing the delegation caches does not end up emptying the role member list or by-ID caches.
871 			getIdentityManagementNotificationService().delegationUpdated();
872 			assertRoleMemberListsAreCachedAsExpected(daoAction, roleIds, principalId, groupIds, memberTypeCode, expectedCachedMembers, true);
873 			secondMemberMap = convertKimObjectListToMap(
874 					getCorrectRoleMemberImplList(daoAction, roleIds, principalId, groupIds, memberTypeCode), ROLE_MEMBER_IMPL_CHECKER);
875 			assertKimObjectResultsAreEqual(expectedMembers.size(), firstMemberMap, secondMemberMap, ROLE_MEMBER_IMPL_CHECKER);
876 			
877 			// Clean up the cache when done.
878 			getIdentityManagementNotificationService().roleUpdated();
879 		}
880 		
881 		/*
882 		 * A convenience method for checking whether or not certain role member lists are in the cache, as well as whether or not the role members from
883 		 * the cached lists are also cached individually.
884 		 */
885 		private void assertRoleMemberListsAreCachedAsExpected(RoleDaoAction daoAction, List<String> roleIds, String principalId,
886 				List<String> groupIds, String memberTypeCode, List<String> expectedMembers, boolean shouldBeInCache) {
887 			Map<String,RoleMemberBo> cachedMembers = new HashMap<String,RoleMemberBo>();
888 			// Generate the cache keys to use.
889 			List<String[]> cacheKeyHelp = new ArrayList<String[]>();
890 			if (roleIds == null || roleIds.isEmpty()) { roleIds = Collections.singletonList(null); }
891 			if (groupIds == null || groupIds.isEmpty()) { groupIds = Collections.singletonList(null); }
892 			switch (daoAction) {
893 				case ROLE_PRINCIPALS_FOR_PRINCIPAL_ID_AND_ROLE_IDS : // Search for principal role members only.
894 					for (String roleId : roleIds) {
895 						cacheKeyHelp.add(new String[] {roleId, getRoleMemberListCacheKey(daoAction, roleId, principalId, null, null)} );
896 					}
897 					break;
898 				case ROLE_GROUPS_FOR_GROUP_IDS_AND_ROLE_IDS : // Search for group role members only.
899 					for (String roleId : roleIds) {
900 						for (String groupId : groupIds) {
901 							cacheKeyHelp.add(new String[] {roleId, getRoleMemberListCacheKey(daoAction, roleId, null, groupId, null)} );
902 						}
903 					}
904 					break;
905 				case ROLE_MEMBERS_FOR_ROLE_IDS : // Search for role members with the given member type code.
906 					for (String roleId : roleIds) {
907 						cacheKeyHelp.add(new String[] {roleId, getRoleMemberListCacheKey(daoAction, roleId, null, null, memberTypeCode)} );
908 					}
909 					break;
910 				case ROLE_MEMBERSHIPS_FOR_ROLE_IDS_AS_MEMBERS : // Search for role members who are also roles.
911 					for (String roleId : roleIds) {
912 						cacheKeyHelp.add(new String[] {roleId, getRoleMemberListCacheKey(daoAction, roleId, null, null, null)} );
913 					}
914 					break;
915 				case ROLE_MEMBERS_FOR_ROLE_IDS_WITH_FILTERS : // Search for role members that might be roles, principals, or groups.
916 					for (String roleId : roleIds) {
917 						cacheKeyHelp.add(new String[] {roleId, getRoleMemberListCacheKey(daoAction, roleId, null, null, Role.ROLE_MEMBER_TYPE)} );
918 						cacheKeyHelp.add(new String[] {roleId, getRoleMemberListCacheKey(daoAction, roleId, principalId, null,Role.PRINCIPAL_MEMBER_TYPE)});
919 						for (String groupId : groupIds) {
920 							cacheKeyHelp.add( new String[] {roleId, getRoleMemberListCacheKey(daoAction, roleId, null, groupId, Role.GROUP_MEMBER_TYPE)} );
921 						}
922 					}
923 					break;
924 				default : // The daoActionToTake parameter is invalid; throw an exception.
925 					throw new IllegalArgumentException("The 'daoActionToTake' parameter cannot be a non-role-member-related value!");
926 			}
927 			
928 			// Ensure that the lists are present or absent from the cache as expected.
929 			for (String[] cacheData : cacheKeyHelp) {
930 				List<RoleMemberBo> cachedList = (List<RoleMemberBo>) getCacheAdministrator().getFromCache(cacheData[1]);
931 				if (cachedList == null) {
932 					if (shouldBeInCache && listShouldBeAllowedInCache(daoAction, cacheData[0])) {
933 						fail("The role member list with key '" + cacheData[1] + "' should have been cached");
934 					}
935 				} else if (!shouldBeInCache || !listShouldBeAllowedInCache(daoAction, cacheData[0])) {
936 					fail("The role member list with key '" + cacheData[1] +
937 							((shouldBeInCache) ? "' should have been excluded from the cache" : "' should not have been cached"));
938 				} else {
939 					for (RoleMemberBo cachedObject : cachedList) { cachedMembers.put(cachedObject.getRoleMemberId(), cachedObject); }
940 				}
941 			}
942 			
943 			// Ensure that the expected role members were found in (or were absent from) the list cache and the by-ID cache.
944 			if (shouldBeInCache) {
945 				assertEquals("The wrong number of role members were obtained from the role member list cache.",expectedMembers.size(),cachedMembers.size());
946 				for (String expectedRoleMemberId : expectedMembers) {
947 					assertNotNull("The role member lists from the cache did not contain the role member with ID " + expectedRoleMemberId,
948 							cachedMembers.get(expectedRoleMemberId));
949 				}
950 			} else {
951 				assertEquals("No role members should have been obtained from the role member list cache.", 0, cachedMembers.size());
952 			}
953 			assertKimObjectsAreCachedByIdAsExpected(expectedMembers, shouldBeInCache, ROLE_MEMBER_IMPL_CHECKER);
954 		}
955 		
956 		// --------------------------------------------------------------------------------------------------------------------
957 		// Convenience methods related to KimDelegationImpl caching.
958 		// --------------------------------------------------------------------------------------------------------------------
959 		
960 		/*
961 		 * A convenience method for checking whether or not certain KimDelegationImpl Lists are being cached.
962 		 */
963 		private void assertDelegationListCachingIsWorking(List<String> roleIds, List<String> expectedDelegations, boolean getsMaps) throws Exception {
964 			// Ensure that the KimDelegationImpl lists are getting cached as expected.
965 			Map<String,DelegateBo> firstDelegationMap = (getsMaps) ? getStoredDelegationImplMapFromRoleIds(roleIds) :
966 				convertKimObjectListToMap(getStoredDelegationImplsForRoleIds(roleIds), KIM_DELEGATION_IMPL_CHECKER);
967 			assertDelegationListsAreCachedAsExpected(roleIds, expectedDelegations, true);
968 			Map<String,DelegateBo> secondDelegationMap = (getsMaps) ? getStoredDelegationImplMapFromRoleIds(roleIds) :
969 				convertKimObjectListToMap(getStoredDelegationImplsForRoleIds(roleIds), KIM_DELEGATION_IMPL_CHECKER);
970 			assertKimObjectResultsAreEqual(expectedDelegations.size(), firstDelegationMap, secondDelegationMap, KIM_DELEGATION_IMPL_CHECKER);
971 			
972 			// Ensure that the KimDelegationImpl lists can be cleared from the cache properly when roleUpdated() is called.
973 			getIdentityManagementNotificationService().roleUpdated();
974 			assertDelegationListsAreCachedAsExpected(roleIds, expectedDelegations, false);
975 			firstDelegationMap = (getsMaps) ? getStoredDelegationImplMapFromRoleIds(roleIds) :
976 				convertKimObjectListToMap(getStoredDelegationImplsForRoleIds(roleIds), KIM_DELEGATION_IMPL_CHECKER);
977 			assertKimObjectResultsAreEqual(expectedDelegations.size(), secondDelegationMap, firstDelegationMap, KIM_DELEGATION_IMPL_CHECKER);
978 			
979 			// Ensure that the KimDelegationImpl lists are cleared out when delegationUpdated() is called.
980 			getIdentityManagementNotificationService().delegationUpdated();
981 			assertDelegationListsAreCachedAsExpected(roleIds, expectedDelegations, false);
982 			secondDelegationMap = (getsMaps) ? getStoredDelegationImplMapFromRoleIds(roleIds) :
983 				convertKimObjectListToMap(getStoredDelegationImplsForRoleIds(roleIds), KIM_DELEGATION_IMPL_CHECKER);
984 			assertKimObjectResultsAreEqual(expectedDelegations.size(), firstDelegationMap, secondDelegationMap, KIM_DELEGATION_IMPL_CHECKER);
985 			
986 			// Clean up the cache when done.
987 			getIdentityManagementNotificationService().roleUpdated();
988 		}
989 		
990 		/*
991 		 * A convenience method for checking whether or not certain delegation Lists are in the cache.
992 		 */
993 		private void assertDelegationListsAreCachedAsExpected(List<String> roleIds, List<String> expectedDelegations, boolean shouldBeInCache) {
994 			Map<String,DelegateBo> cachedDelegations = new HashMap<String,DelegateBo>();
995 			
996 			// Ensure that the lists are present or absent from the cache as expected.
997 			if (roleIds != null && !roleIds.isEmpty()) {
998 				for (String roleId : roleIds) {
999 					List<DelegateBo> cachedList = (List<DelegateBo>) getCacheAdministrator().getFromCache(getDelegationListCacheKey(roleId));
1000 					if (cachedList == null) {
1001 						if (shouldBeInCache) {
1002 							fail("The delegation list for role ID '" + roleId + "' should have been cached");
1003 						}
1004 					} else if (!shouldBeInCache) {
1005 						fail("The delegation list for role ID '" + roleId + "' should not have been cached");
1006 					} else {
1007 						for (DelegateBo cachedObject : cachedList) { cachedDelegations.put(cachedObject.getDelegationId(), cachedObject); }
1008 					}
1009 				}
1010 			}
1011 			
1012 			// Ensure that the expected delegations were found in (or were absent from) the list cache and the by-ID cache.
1013 			if (shouldBeInCache) {
1014 				assertEquals("The wrong number of delegations were obtained from the delegation list cache.",
1015 						expectedDelegations.size(), cachedDelegations.size());
1016 				for (String expectedDelegationId : expectedDelegations) {
1017 					assertNotNull("The delegation lists from the cache did not contain the delegation with ID " + expectedDelegationId,
1018 							cachedDelegations.get(expectedDelegationId));
1019 				}
1020 			} else {
1021 				assertEquals("No delegations should have been obtained from the delegation list cache.", 0, cachedDelegations.size());
1022 			}
1023 			assertKimObjectsAreCachedByIdAsExpected(expectedDelegations, shouldBeInCache, KIM_DELEGATION_IMPL_CHECKER);
1024 		}
1025 		
1026 		// --------------------------------------------------------------------------------------------------------------------
1027 		// Convenience methods related to KimDelegationMemberImpl caching.
1028 		// --------------------------------------------------------------------------------------------------------------------
1029 
1030 		/*
1031 		 * A convenience method for retrieving Maps of delegation members by delegation and delegation member ID.
1032 		 */
1033 		private Map<String,DelegateMemberBo> getKimDelegationMemberImplMapByIdAndDelegationId(String[][] idArray) {
1034 			Map<String,DelegateMemberBo> finalResults = new HashMap<String,DelegateMemberBo>();
1035 			for (String[] ids : idArray) {
1036 				DelegateMemberBo delegationMember = getKimDelegationMemberImplByDelegationAndId(ids[0], ids[1]);
1037 				if (delegationMember != null && delegationMember.isActive(new Timestamp(System.currentTimeMillis()))) {
1038 					finalResults.put(delegationMember.getDelegationMemberId(), delegationMember);
1039 				}
1040 			}
1041 			return finalResults;
1042 		}
1043 		
1044 		/*
1045 		 * A convenience method for retrieving Maps of delegation members by member ID and delegation ID.
1046 		 */
1047 		private Map<String,DelegateMemberBo> getKimDelegationMemberImplMapByMemberAndDelegationId(String[][] idArray) {
1048 			Map<String,DelegateMemberBo> finalResults = new HashMap<String,DelegateMemberBo>();
1049 			for (String[] ids : idArray) {
1050 				List<DelegateMemberBo> memberList = getDelegationMemberListByMemberAndDelegationIdFromCache(ids[0], ids[1]);
1051 				if (memberList != null && !memberList.isEmpty()) {
1052 					for (DelegateMemberBo delegationMember : memberList) {
1053 						if (delegationMember != null && delegationMember.isActive(new Timestamp(System.currentTimeMillis()))) {
1054 							finalResults.put(delegationMember.getDelegationMemberId(), delegationMember);
1055 						}
1056 					}
1057 				}
1058 			}
1059 			return finalResults;
1060 		}
1061 		
1062 		/*
1063 		 * A convenience method for testing whether or not delegation members obtained by delegation ID and delegation member ID are cached as expected.
1064 		 */
1065 		private void assertDelegationMembersAreCachedByIdAndDelegationIdAsExpected(String[][] idArray, boolean shouldBeInCache) throws Exception {
1066 			for (String[] ids : idArray) {
1067 				DelegateMemberBo tempMember = getDelegationMemberByDelegationAndIdFromCache(ids[0], ids[1]);
1068 				if (shouldBeInCache) {
1069 					if (tempMember == null) {
1070 						assertNotNull("Delegation member with ID '" + ids[1] + "' and belonging to delegation with ID '" + ids[0] +
1071 								"' should have been in the cache", tempMember);
1072 					}
1073 				} else if (tempMember != null) {
1074 					assertNull("Delegation member with ID '" + ids[1] + "' and belonging to delegation with ID '" + ids[0] +
1075 							"' should not have been in the cache", tempMember);
1076 				}
1077 			}
1078 		}
1079 		
1080 		/*
1081 		 * A convenience method for testing whether or not delegation member lists obtained by member and delegation ID are cached as expected.
1082 		 */
1083 		private void assertDelegationMemberListsAreCachedByMemberAndDelegationIdAsExpected(String[][] idArray, boolean shouldBeInCache) throws Exception {
1084 			for (String[] ids : idArray) {
1085 				List<DelegateMemberBo> tempList = getDelegationMemberListByMemberAndDelegationIdFromCache(ids[0], ids[1]);
1086 				if (shouldBeInCache) {
1087 					if (tempList == null) {
1088 						assertNotNull("Delegation member list for member ID '" + ids[0] + "' and delegation ID '" + ids[1] +
1089 								"' should have been in the cache", tempList);
1090 					} else {
1091 						if (ids.length - 2 != tempList.size()) {
1092 							assertEquals("Delegation member list for member ID '" + ids[0] + "' and delegation ID '" + ids[1] +
1093 									"' contains the wrong number of elements.", ids.length - 2, tempList.size());
1094 						} else {
1095 							for (int i = 2; i < ids.length; i++) {
1096 								boolean foundMember = false;
1097 								for (DelegateMemberBo tempMember : tempList) {
1098 									if (tempMember.getDelegationMemberId().equals(ids[i])) {
1099 										foundMember = true;
1100 										break;
1101 									}
1102 								}
1103 								if (!foundMember) {
1104 									fail("Delegation member list for member ID '" + ids[0] + "' and delegation ID '" + ids[1] +
1105 											"' should have contained the delegation member with ID " + ids[i]);
1106 								}
1107 							}
1108 						}
1109 					}
1110 				} else if (tempList != null) {
1111 					assertNull("Delegation member list for member ID '" + ids[0] + "' and delegation ID '" + ids[1] +
1112 							"' should not have been in the cache", tempList);
1113 				}
1114 			}
1115 		}
1116 		
1117 		/*
1118 		 * A convenience method for choosing the correct helper method for retrieving delegation member lists (or maps converted to lists).
1119 		 */
1120 		private List<DelegateMemberBo> getCorrectKimDelegationMemberImplList(RoleDaoAction daoAction, List<String> delegationIds,
1121 				String principalId, List<String> groupIds) {
1122 			switch (daoAction) {
1123 				case DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS :
1124 					return getStoredDelegationPrincipalsForPrincipalIdAndDelegationIds(delegationIds, principalId);
1125 				case DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS :
1126 					return getStoredDelegationGroupsForGroupIdsAndDelegationIds(delegationIds, groupIds);
1127 				case DELEGATION_MEMBERS_FOR_DELEGATION_IDS :
1128 					Map<String,List<DelegateMemberBo>> memberMap = getStoredDelegationMembersForDelegationIds(delegationIds);
1129 					List<DelegateMemberBo> finalResults = new ArrayList<DelegateMemberBo>();
1130 					for (List<DelegateMemberBo> subList : memberMap.values()) {
1131 						finalResults.addAll(subList);
1132 					}
1133 					return finalResults;
1134 				default : throw new IllegalArgumentException("The 'daoAction' parameter cannot be a non-delegation-member-related value!");
1135 			}
1136 		}
1137 		
1138 		/*
1139 		 * A convenience method for checking whether or not certain KimDelegationMemberImpl Lists are being cached.
1140 		 */
1141 		private void assertDelegationMemberListCachingIsWorking(RoleDaoAction daoAction, List<String> delegationIds, String principalId,
1142 				List<String> groupIds, String[] expectedMembersArray) throws Exception {
1143 			List<String> expectedMembers = Arrays.asList(expectedMembersArray);
1144 			// Ensure that the KimDelegationMemberImpl lists are getting cached as expected.
1145 			Map<String,DelegateMemberBo> firstMemberMap = convertKimObjectListToMap(getCorrectKimDelegationMemberImplList(
1146 					daoAction, delegationIds, principalId, groupIds), KIM_DLGN_MBR_IMPL_CHECKER);
1147 			assertDelegationMemberListsAreCachedAsExpected(daoAction, delegationIds, principalId, groupIds, expectedMembers, true);
1148 			Map<String,DelegateMemberBo> secondMemberMap = convertKimObjectListToMap(getCorrectKimDelegationMemberImplList(
1149 					daoAction, delegationIds, principalId, groupIds), KIM_DLGN_MBR_IMPL_CHECKER);
1150 			assertKimObjectResultsAreEqual(expectedMembers.size(), firstMemberMap, secondMemberMap, KIM_DLGN_MBR_IMPL_CHECKER);
1151 			
1152 			// Ensure that the KimDelegationMemberImpl lists can be cleared from the cache properly when roleUpdated() is called.
1153 			getIdentityManagementNotificationService().roleUpdated();
1154 			assertDelegationMemberListsAreCachedAsExpected(daoAction, delegationIds, principalId, groupIds, expectedMembers, false);
1155 			firstMemberMap = convertKimObjectListToMap(getCorrectKimDelegationMemberImplList(
1156 					daoAction, delegationIds, principalId, groupIds), KIM_DLGN_MBR_IMPL_CHECKER);
1157 			assertKimObjectResultsAreEqual(expectedMembers.size(), secondMemberMap, firstMemberMap, KIM_DLGN_MBR_IMPL_CHECKER);
1158 			
1159 			// Ensure that the KimDelegationMemberImpl lists are cleared out when delegationUpdated() is called.
1160 			getIdentityManagementNotificationService().delegationUpdated();
1161 			assertDelegationMemberListsAreCachedAsExpected(daoAction, delegationIds, principalId, groupIds, expectedMembers, false);
1162 			secondMemberMap = convertKimObjectListToMap(getCorrectKimDelegationMemberImplList(
1163 					daoAction, delegationIds, principalId, groupIds), KIM_DLGN_MBR_IMPL_CHECKER);
1164 			assertKimObjectResultsAreEqual(expectedMembers.size(), firstMemberMap, secondMemberMap, KIM_DLGN_MBR_IMPL_CHECKER);
1165 			
1166 			// Clean up the cache when done.
1167 			getIdentityManagementNotificationService().roleUpdated();
1168 		}
1169 		
1170 		/*
1171 		 * A convenience method for checking whether or not certain delegation member Lists are in the cache.
1172 		 */
1173 		private void assertDelegationMemberListsAreCachedAsExpected(RoleDaoAction daoAction, List<String> delegationIds, String principalId,
1174 				List<String> groupIds, List<String> expectedMembers, boolean shouldBeInCache) {
1175 			Map<String,DelegateMemberBo> cachedMembers = new HashMap<String,DelegateMemberBo>();
1176 			List<String[]> cacheKeys = new ArrayList<String[]>();
1177 			if (delegationIds == null || delegationIds.isEmpty()) { delegationIds = Collections.singletonList(null); }
1178 			if (groupIds == null || groupIds.isEmpty()) { groupIds = Collections.singletonList(null); }
1179 			
1180 			// Create the cache key generation parameters based on the intended KimRoleDao action.
1181 			switch (daoAction) {
1182 				case DELEGATION_PRINCIPALS_FOR_PRINCIPAL_ID_AND_DELEGATION_IDS : // Search for principal delegation members.
1183 					for (String delegationId : delegationIds) {
1184 						cacheKeys.add(new String[] {delegationId, principalId, null});
1185 					}
1186 					break;
1187 				case DELEGATION_GROUPS_FOR_GROUP_IDS_AND_DELEGATION_IDS : // Search for group delegation members.
1188 					for (String delegationId : delegationIds) {
1189 						for (String groupId : groupIds) {
1190 							cacheKeys.add(new String[] {delegationId, null, groupId});
1191 						}
1192 					}
1193 					break;
1194 				case DELEGATION_MEMBERS_FOR_DELEGATION_IDS : // Search for delegation members regardless of their member type.
1195 					for (String delegationId : delegationIds) {
1196 						cacheKeys.add(new String[] {delegationId, null, null});
1197 					}
1198 					break;
1199 				default : // daoAction is invalid; throw an exception.
1200 					throw new IllegalArgumentException("The 'daoAction' parameter cannot refer to a non-delegation-member-related value!");
1201 			}
1202 			
1203 			// Ensure that the lists are present or absent from the cache as expected.
1204 			for (String[] cacheKey : cacheKeys) {
1205 				List<DelegateMemberBo> cachedList = (List<DelegateMemberBo>) getCacheAdministrator().getFromCache(
1206 						getDelegationMemberListCacheKey(daoAction, cacheKey[0], cacheKey[1], cacheKey[2]));
1207 				if (cachedList == null) {
1208 					if (shouldBeInCache) {
1209 						fail("The delegation member list with key '" +
1210 								getDelegationMemberListCacheKey(daoAction, cacheKey[0], cacheKey[1], cacheKey[2]) + "' should have been cached");
1211 					}
1212 				} else if (!shouldBeInCache) {
1213 					fail("The delegation member list with key '" +
1214 							getDelegationMemberListCacheKey(daoAction, cacheKey[0], cacheKey[1], cacheKey[2]) + "' should not have been cached");
1215 				} else {
1216 					for (DelegateMemberBo cachedObject : cachedList) { cachedMembers.put(cachedObject.getDelegationMemberId(), cachedObject); }
1217 				}
1218 			}
1219 			
1220 			// Ensure that the expected delegation members were found in (or were absent from) the list cache and the by-ID cache.
1221 			if (shouldBeInCache) {
1222 				assertEquals("The wrong number of delegation members were obtained from the delegation member list cache.",
1223 						expectedMembers.size(), cachedMembers.size());
1224 				for (String expectedDelegationMemberId : expectedMembers) {
1225 					assertNotNull("The delegation member lists from the cache did not contain the delegation member with ID " + expectedDelegationMemberId,
1226 							cachedMembers.get(expectedDelegationMemberId));
1227 				}
1228 			} else {
1229 				assertEquals("No delegation members should have been obtained from the delegation member list cache.", 0, cachedMembers.size());
1230 			}
1231 			assertKimObjectsAreCachedByIdAsExpected(expectedMembers, shouldBeInCache, KIM_DLGN_MBR_IMPL_CHECKER);
1232 		}
1233 	}
1234 }