View Javadoc

1   /**
2    * Copyright 2005-2011 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.krad.service;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.junit.Test;
20  import org.kuali.rice.kew.api.exception.WorkflowException;
21  import org.kuali.rice.kim.api.KimConstants.PermissionNames;
22  import org.kuali.rice.kim.api.identity.Person;
23  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
24  import org.kuali.rice.kns.authorization.AuthorizationConstants;
25  import org.kuali.rice.krad.UserSession;
26  import org.kuali.rice.krad.document.Document;
27  import org.kuali.rice.krad.maintenance.MaintenanceDocument;
28  import org.kuali.rice.krad.document.authorization.PessimisticLock;
29  import org.kuali.rice.krad.exception.AuthorizationException;
30  import org.kuali.rice.krad.service.impl.PessimisticLockServiceImpl;
31  import org.kuali.rice.krad.test.document.AccountRequestDocument;
32  import org.kuali.rice.krad.test.document.AccountRequestDocument2;
33  import org.kuali.rice.krad.util.GlobalVariables;
34  import org.kuali.rice.krad.util.KRADConstants;
35  import org.kuali.rice.krad.util.KRADPropertyConstants;
36  import org.kuali.rice.maintainable.AccountType2MaintainableImpl;
37  import org.kuali.rice.test.BaselineTestCase;
38  import org.kuali.rice.test.data.UnitTestData;
39  import org.kuali.rice.test.data.UnitTestSql;
40  import org.kuali.test.KRADTestCase;
41  
42  import java.io.Serializable;
43  import java.util.Arrays;
44  import java.util.Collections;
45  import java.util.HashMap;
46  import java.util.HashSet;
47  import java.util.List;
48  import java.util.Map;
49  import java.util.Set;
50  
51  import static org.junit.Assert.*;
52  
53  
54  /**
55   * This class is used to test the {@link PessimisticLockServiceImpl} class
56   *
57   * @author Kuali Rice Team (rice.collab@kuali.org)
58   */
59  @BaselineTestCase.BaselineMode(BaselineTestCase.Mode.NONE)
60  public class PessimisticLockServiceTest extends KRADTestCase {
61  
62      @Override
63      public void setUp() throws Exception {
64          super.setUp();
65          GlobalVariables.setUserSession(new UserSession("quickstart"));
66      }
67  
68      /**
69       * This method tests deleting {@link PessimisticLock} objects. Tests that invalid deletes throw exceptions and valid
70       * deletes by owner users as well as lock admin users do work as expected
71       *
72       * @throws Exception
73       */
74      @UnitTestData(
75              sqlStatements = {
76                      @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
77                      @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1111, '4f6bc9e2-7df8-102c-97b6-ed716fdaf540', 0, NULL, '1234', {d '2007-07-01'}, 'employee')"),
78                      @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1112, '5add9cba-7df8-102c-97b6-ed716fdaf540', 0, NULL, '1235', {d '2007-10-01'}, 'frank')"),
79                      @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1113, '69e42b8e-7df8-102c-97b6-ed716fdaf540', 0, NULL, '1236', {d '2007-08-01'}, 'fred')"),
80                      @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1114, '76504650-7df8-102c-97b6-ed716fdaf540', 0, NULL, '1237', {d '2007-08-01'}, 'fred')")
81                      }
82              )
83      @Test
84      public void testDeleteLocks() throws Exception {
85      	
86          List<PessimisticLock> locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
87          assertEquals("Should be 4 locks in DB", 4, locks.size());
88  
89          String userId = "employee";
90          String[] lockIdsToVerify = new String[]{"1112", "1113"};
91          assertFalse("User " + userId + " should not be member of pessimistic lock admin permission", KimApiServiceLocator.getPermissionService().isAuthorized(new UserSession(userId).getPerson().getPrincipalId(), KRADConstants.KRAD_NAMESPACE, PermissionNames.ADMIN_PESSIMISTIC_LOCKING,
92                  Collections.<String, String>emptyMap(), Collections.<String, String>emptyMap() ) );
93          verifyDelete(userId, Arrays.asList(lockIdsToVerify), AuthorizationException.class, true);
94          userId = "frank";
95          lockIdsToVerify = new String[]{"1111", "1113"};
96          assertFalse("User " + userId + " should not be member of pessimistic lock admin permission", KimApiServiceLocator.getPermissionService().isAuthorized(new UserSession(userId).getPerson().getPrincipalId(), KRADConstants.KRAD_NAMESPACE, PermissionNames.ADMIN_PESSIMISTIC_LOCKING, Collections.<String, String>emptyMap(), Collections.<String, String>emptyMap() ) );
97          verifyDelete(userId, Arrays.asList(lockIdsToVerify), AuthorizationException.class, true);
98          userId = "fred";
99          lockIdsToVerify = new String[]{"1111", "1112"};
100         assertFalse("User " + userId + " should not be member of pessimistic lock admin permission", KimApiServiceLocator.getPermissionService().isAuthorized(new UserSession(userId).getPerson().getPrincipalId(), KRADConstants.KRAD_NAMESPACE, PermissionNames.ADMIN_PESSIMISTIC_LOCKING, Collections.<String, String>emptyMap(), Collections.<String, String>emptyMap() ) );
101         verifyDelete(userId, Arrays.asList(lockIdsToVerify), AuthorizationException.class, true);
102 
103         verifyDelete("employee", Arrays.asList(new String[]{"1111"}), null, false);
104         verifyDelete("frank", Arrays.asList(new String[]{"1112"}), null, false);
105         verifyDelete("fred", Arrays.asList(new String[]{"1113"}), null, false);
106         locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
107         assertEquals("Should be 1 lock left in DB", 1, locks.size());
108 
109         // test admin user can delete any lock
110         userId = "fran";
111         assertTrue("User " + userId + " should be member of pessimistic lock admin permission", KimApiServiceLocator.getPermissionService().isAuthorized(new UserSession(userId).getPerson().getPrincipalId(), KRADConstants.KRAD_NAMESPACE, PermissionNames.ADMIN_PESSIMISTIC_LOCKING, Collections.<String, String>emptyMap(), Collections.<String, String>emptyMap() ) );
112         userId = "admin";
113         assertTrue("User " + userId + " should be member of pessimistic lock admin permission", KimApiServiceLocator.getPermissionService().isAuthorized(new UserSession(userId).getPerson().getPrincipalId(), KRADConstants.KRAD_NAMESPACE, PermissionNames.ADMIN_PESSIMISTIC_LOCKING, Collections.<String, String>emptyMap(), Collections.<String, String>emptyMap() ) );
114         verifyDelete(userId, Arrays.asList(new String[]{"1114"}), null, false);
115         locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
116         assertEquals("Should be 0 locks left in DB", 0, locks.size());
117     }
118 
119     private void verifyDelete(String userId, List<String> lockIds, Class expectedException, boolean expectException) throws WorkflowException {
120         GlobalVariables.setUserSession(new UserSession(userId));
121         for (String lockId : lockIds) {
122             try {
123                 KRADServiceLocatorWeb.getPessimisticLockService().delete(lockId);
124                 if (expectException) {
125                     fail("Expected exception when deleting lock with id '" + lockId + "' for user '" + userId + "'");
126                 }
127             } catch (Exception e) {
128                 if (!expectException) {
129                     fail("Did not expect exception when deleting lock with id '" + lockId + "' for user '" + userId + "' but got exception of type '" + e.getClass().getName() + "'");
130                 }
131                 if (expectedException != null) {
132                     // if we have an expected exception
133                     if (!expectedException.isAssignableFrom(e.getClass())) {
134                         fail("Expected exception of type '" + expectedException.getName() + "' when deleting lock with id '" + lockId + "' for user '" + userId + "' but got exception of type '" + e.getClass().getName() + "'");
135                     }
136                 }
137             }
138         }
139     }
140 
141     /**
142      * This method tests the generation of new {@link PessimisticLock} objects
143      *
144      * @throws Exception
145      */
146     @Test
147     public void testGenerateNewLocks() throws Exception {
148         PessimisticLockService lockService = KRADServiceLocatorWeb.getPessimisticLockService();
149 
150         // test generating lock with no given lock descriptor
151         String documentNumber = "1243";
152         PessimisticLock lock = lockService.generateNewLock(documentNumber);
153         assertNotNull("Generated lock should have id", lock.getId());
154         assertEquals("Document Number should match", documentNumber, lock.getDocumentNumber());
155         assertNotNull("Generated lock should have a generated timestamp ", lock.getGeneratedTimestamp());
156         assertEquals("Generated lock should have default lock descriptor", PessimisticLock.DEFAULT_LOCK_DESCRIPTOR, lock.getLockDescriptor());
157         assertEquals("Generated lock should be owned by current user", GlobalVariables.getUserSession().getPerson().getPrincipalName(), lock.getOwnedByUser().getPrincipalName());
158         Map primaryKeys = new HashMap();
159         primaryKeys.put(KRADPropertyConstants.ID, lock.getId());
160         lock = null;
161         lock = (PessimisticLock) KRADServiceLocator.getBusinessObjectService().findByPrimaryKey(PessimisticLock.class, primaryKeys);
162         assertNotNull("Generated lock should be available from BO Service", lock);
163         assertNotNull("Generated lock should have id", lock.getId());
164         assertEquals("Document Number should match", documentNumber, lock.getDocumentNumber());
165         assertNotNull("Generated lock should have a generated timestamp ", lock.getGeneratedTimestamp());
166         assertEquals("Generated lock should have default lock descriptor", PessimisticLock.DEFAULT_LOCK_DESCRIPTOR, lock.getLockDescriptor());
167         assertEquals("Generated lock should be owned by current user", GlobalVariables.getUserSession().getPerson().getPrincipalName(), lock.getOwnedByUser().getPrincipalName());
168 
169         // test generating lock with given lock descriptor
170         lock = null;
171         documentNumber = "4321";
172         String lockDescriptor = "this is a test lock descriptor";
173         lock = lockService.generateNewLock(documentNumber, lockDescriptor);
174         assertNotNull("Generated lock should have id", lock.getId());
175         assertEquals("Document Number should match", documentNumber, lock.getDocumentNumber());
176         assertNotNull("Generated lock should have a generated timestamp ", lock.getGeneratedTimestamp());
177         assertEquals("Generated lock should have lock descriptor set", lockDescriptor, lock.getLockDescriptor());
178         assertEquals("Generated lock should be owned by current user", GlobalVariables.getUserSession().getPerson().getPrincipalName(), lock.getOwnedByUser().getPrincipalName());
179         primaryKeys = new HashMap();
180         primaryKeys.put(KRADPropertyConstants.ID, lock.getId());
181         lock = null;
182         lock = (PessimisticLock) KRADServiceLocator.getBusinessObjectService().findByPrimaryKey(PessimisticLock.class, primaryKeys);
183         assertNotNull("Generated lock should be available from BO Service", lock);
184         assertNotNull("Generated lock should have id", lock.getId());
185         assertEquals("Document Number should match", documentNumber, lock.getDocumentNumber());
186         assertNotNull("Generated lock should have a generated timestamp ", lock.getGeneratedTimestamp());
187         assertEquals("Generated lock should have lock descriptor set", lockDescriptor, lock.getLockDescriptor());
188         assertEquals("Generated lock should be owned by current user", GlobalVariables.getUserSession().getPerson().getPrincipalName(), lock.getOwnedByUser().getPrincipalName());
189     }
190 
191     /**
192      * This method tests retrieving {@link PessimisticLock} objects by document number
193      *
194      * @throws Exception
195      */
196     @UnitTestData(
197             sqlStatements = {
198                     @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
199                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1111, 'fbcb0362-7dfb-102c-97b6-ed716fdaf540', 0, NULL, '1234', {d '2007-07-01'}, 'fran')"),
200                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1112, '055bef4a-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1237', {d '2007-10-01'}, 'frank')"),
201                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1113, '0e0144ec-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1236', {d '2007-10-01'}, 'frank')"),
202                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1114, '1891526c-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1237', {d '2007-08-01'}, 'fred')")
203                     }
204             )
205     @Test
206     public void testGetPessimisticLocksForDocument() throws Exception {
207         PessimisticLockService lockService = KRADServiceLocatorWeb.getPessimisticLockService();
208         String docId = "1234";
209         assertEquals("Document " + docId + " expected lock count incorrect", 1, lockService.getPessimisticLocksForDocument(docId).size());
210         docId = "1237";
211         assertEquals("Document " + docId + " expected lock count incorrect", 2, lockService.getPessimisticLocksForDocument(docId).size());
212         docId = "1236";
213         assertEquals("Document " + docId + " expected lock count incorrect", 1, lockService.getPessimisticLocksForDocument(docId).size());
214         docId = "3948";
215         assertEquals("Document " + docId + " expected lock count incorrect", 0, lockService.getPessimisticLocksForDocument(docId).size());
216     }
217 
218     /**
219      * This method tests releasing {@link PessimisticLock} objects for a specific user
220      *
221      * @throws Exception
222      */
223     @UnitTestData(
224             sqlStatements = {
225                     @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
226                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1111, '24c40cd2-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1234', {d '2007-07-01'}, 'fran')"),
227                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1112, '32602e8e-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1235', {d '2007-10-01'}, 'frank')"),
228                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1113, '3acfc1ce-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1236', {d '2007-10-01'}, 'frank')"),
229                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1114, '463cc642-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1237', {d '2007-08-01'}, 'fred')"),
230                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1115, '4e66c4b2-7dfc-102c-97b6-ed716fdaf540', 0, 'Temporary Lock', '1234', {d '2007-07-01'}, 'fran')"),
231                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1116, '55d99b02-7dfc-102c-97b6-ed716fdaf540', 0, 'Temporary Lock', '1235', {d '2007-10-01'}, 'frank')"),
232                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1117, '5e47fb26-7dfc-102c-97b6-ed716fdaf540', 0, 'Temporary Lock', '1236', {d '2007-10-01'}, 'frank')"),
233                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1118, '65c366d8-7dfc-102c-97b6-ed716fdaf540', 0, 'Temporary Lock', '1237', {d '2007-08-01'}, 'fred')")
234                     }
235             )
236     @Test
237     public void testReleaseAllLocksForUser() throws Exception {
238         String lockDescriptor = "Temporary Lock";
239         List<PessimisticLock> locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
240         assertEquals("Should be 8 manually inserted locks", 8, locks.size());
241 
242         KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("fran"), lockDescriptor);
243         locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
244         assertEquals("Should be 7 locks left after releasing locks for fran using lock descriptor " + lockDescriptor, 7, locks.size());
245 
246         KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("frank"), lockDescriptor);
247         locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
248         assertEquals("Should be 5 locks left after releasing locks for fran and frank using lock descriptor " + lockDescriptor, 5, locks.size());
249 
250         KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("fred"), lockDescriptor);
251         locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
252         assertEquals("Should be 4 locks left after releasing locks for fran, frank, and fred using lock descriptor " + lockDescriptor, 4, locks.size());
253 
254         KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("fran"));
255         locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
256         assertEquals("Should be 3 locks left after releasing locks for fran with no lock descriptor", 3, locks.size());
257 
258         KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("frank"));
259         locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
260         assertEquals("Should be 1 lock left after releasing locks for fran and frank with no lock descriptor", 1, locks.size());
261 
262         KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("fred"));
263         locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
264         assertEquals("Should be no locks left after releasing locks for fran, frank, and fred with no lock descriptor", 0, locks.size());
265     }
266 
267     /**
268      * This method tests saving {@link PessimisticLock} objects
269      *
270      * @throws Exception
271      */
272     @UnitTestData(
273             sqlStatements = {
274                     @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
275                     @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID) VALUES (1111, '73f340de-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1234', {d '2007-07-01'}, 'fran')")
276                     }
277             )
278     @Test
279     public void testSaveLock() throws Exception {
280         String lockDescriptor = "new test lock descriptor";
281         // get existing lock and update lock descriptor and save
282         Map primaryKeys = new HashMap();
283         primaryKeys.put(KRADPropertyConstants.ID, Long.valueOf("1111"));
284         PessimisticLock lock = (PessimisticLock) KRADServiceLocator.getBusinessObjectService().findByPrimaryKey(PessimisticLock.class, primaryKeys);
285         lock.setLockDescriptor(lockDescriptor);
286         KRADServiceLocatorWeb.getPessimisticLockService().save(lock);
287 
288         // verify retrieved lock has lock descriptor set previously
289         PessimisticLock savedLock = (PessimisticLock) KRADServiceLocator.getBusinessObjectService().findByPrimaryKey(PessimisticLock.class, primaryKeys);
290         assertEquals("Lock descriptor is not correct from lock that was saved", lockDescriptor, savedLock.getLockDescriptor());
291     }
292     
293     /**
294      * This method tests the PessimisticLockService.establishLocks method and the PessimisticLockService.getDocumentActions method.
295      * 
296      * @throws Exception
297      */
298     @Test
299     public void testEstablishLocks() throws Exception {
300     	PessimisticLockService lockService = KRADServiceLocatorWeb.getPessimisticLockService();
301     	AccountRequestDocument accountDoc = (AccountRequestDocument) KRADServiceLocatorWeb.getDocumentService().getNewDocument("AccountRequest");
302     	
303     	assertTrue("The AccountRequestDocument should be using pessimistic locking",
304     			KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(accountDoc.getClass().getName()).getUsePessimisticLocking());
305     	
306     	// Have "quickstart" establish a pessimistic lock on the account request document.
307     	UserSession quickstartSession = new UserSession("quickstart");
308     	Person[] quickstartPerson = { quickstartSession.getPerson() };
309     	Map<String,String> editMode = new HashMap<String,String>();
310     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
311     	Map <?,?> finalModes = lockService.establishLocks(accountDoc, editMode, quickstartSession.getPerson());
312     	
313     	// Verify that the lock was actually established.
314     	assertCorrectLocksAreInPlace(true, finalModes, 1, accountDoc.getPessimisticLocks(), quickstartPerson, null);
315     	
316     	// Now check to make sure that a different user (such as "admin") cannot save, route, cancel, or blanket approve the document.
317     	UserSession adminSession = new UserSession("admin");
318     	Set<String> documentActions = new HashSet<String>(Arrays.asList(new String[] { KRADConstants.KUALI_ACTION_CAN_CANCEL,
319     			KRADConstants.KUALI_ACTION_CAN_SAVE, KRADConstants.KUALI_ACTION_CAN_ROUTE, KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE }));
320     	Set<?> finalActions = lockService.getDocumentActions(accountDoc, adminSession.getPerson(), documentActions);
321     	assertFalse("'admin' should not be able to cancel the locked document", finalActions.contains(KRADConstants.KUALI_ACTION_CAN_CANCEL));
322     	assertFalse("'admin' should not be able to save the locked document", finalActions.contains(KRADConstants.KUALI_ACTION_CAN_SAVE));
323     	assertFalse("'admin' should not be able to route the locked document", finalActions.contains(KRADConstants.KUALI_ACTION_CAN_ROUTE));
324     	assertFalse("'admin' should not be able to blanket approve the locked document", finalActions.contains(KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE));
325     	
326     	// Verify that "quickstart" can save, route, and cancel the document since he is the owner of the lock.
327     	documentActions = new HashSet<String>(Arrays.asList(new String[] {
328     			KRADConstants.KUALI_ACTION_CAN_CANCEL, KRADConstants.KUALI_ACTION_CAN_SAVE, KRADConstants.KUALI_ACTION_CAN_ROUTE }));
329     	finalActions = lockService.getDocumentActions(accountDoc, quickstartSession.getPerson(), documentActions);
330     	assertTrue("'quickstart' should have had cancel privileges", finalActions.contains(KRADConstants.KUALI_ACTION_CAN_CANCEL));
331     	assertTrue("'quickstart' should have had save privileges", finalActions.contains(KRADConstants.KUALI_ACTION_CAN_SAVE));
332     	assertTrue("'quickstart' should have had route privileges", finalActions.contains(KRADConstants.KUALI_ACTION_CAN_ROUTE));
333     	
334     	// Check that "admin" cannot establish a lock when one is already in place.
335     	editMode = new HashMap<String,String>();
336     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
337     	finalModes = lockService.establishLocks(accountDoc, editMode, adminSession.getPerson());
338     	assertCorrectLocksAreInPlace(false, finalModes, 1, accountDoc.getPessimisticLocks(), quickstartPerson, null);
339     	
340     	// Make sure that "quickstart" cannot create a second lock if custom lock descriptors are not in use.
341     	editMode = new HashMap<String,String>();
342     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
343     	finalModes = lockService.establishLocks(accountDoc, editMode, quickstartSession.getPerson());
344     	assertCorrectLocksAreInPlace(true, finalModes, 1, accountDoc.getPessimisticLocks(), quickstartPerson, null);
345     }
346     
347     /**
348      * This method tests the PessimistLockService's workflow pessimistic locking capabilities.
349      * 
350      * @throws Exception
351      */
352     @Test
353     public void testWorkflowPessimisticLocking() throws Exception {
354     	PessimisticLockService lockService = KRADServiceLocatorWeb.getPessimisticLockService();
355     	AccountRequestDocument accountDoc = (AccountRequestDocument) KRADServiceLocatorWeb.getDocumentService().getNewDocument("AccountRequest");
356     	assertTrue("The AccountRequestDocument should be using pessimistic locking",
357     			KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDocumentEntry(accountDoc.getClass().getName()).getUsePessimisticLocking());
358     	
359     	// Have the system user create a workflow pessimistic lock.
360     	UserSession systemSession = new UserSession(KRADConstants.SYSTEM_USER);
361     	Person[] systemPerson = { systemSession.getPerson() };
362     	lockService.establishWorkflowPessimisticLocking(accountDoc);
363        	assertCorrectLocksAreInPlace(false, null, 1, accountDoc.getPessimisticLocks(), systemPerson, null);
364        	
365        	// Make sure that no other users can lock when the workflow lock is in place.
366        	UserSession adminSession = new UserSession("admin");
367     	Map<String,String> editMode = new HashMap<String,String>();
368     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
369     	Map<?,?> finalModes = lockService.establishLocks(accountDoc, editMode, adminSession.getPerson());
370     	assertCorrectLocksAreInPlace(false, finalModes, 1, accountDoc.getPessimisticLocks(), systemPerson, null);
371        	
372        	// Ensure that workflow pessimistic locks can also be released.
373        	lockService.releaseWorkflowPessimisticLocking(accountDoc);
374        	assertTrue("There should not be any pessimistic locks present on the document", accountDoc.getPessimisticLocks().isEmpty());
375     }
376     
377     /**
378      * This method tests the PessimisticLockService's ability to establish pessimistic locks for documents supporting custom lock descriptors.
379      * 
380      * @throws Exception
381      */
382     @Test
383     public void testPessimisticLockingWithCustomDocumentLockDescriptors() throws Exception {
384        	AccountRequestDocument2 accountDoc2 = (AccountRequestDocument2) KRADServiceLocatorWeb.getDocumentService().getNewDocument("AccountRequest2");
385        	assertTrue("The AccountRequestDocument2 should be using pessimistic locking", KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary(
386        			).getDocumentEntry(accountDoc2.getClass().getName()).getUsePessimisticLocking());
387        	assertTrue("The AccountRequestDocument2 should be using custom lock descriptors", accountDoc2.useCustomLockDescriptors());
388        	
389        	// Perform the custom lock descriptor unit testing operations.
390        	assertCustomLockDescriptorsAreWorking(accountDoc2, AccountRequestDocument2.ACCT_REQ_DOC_2_EDITABLE_FIELDS,
391        			AccountRequestDocument2.EDIT_ALL_BUT_REASONS, AccountRequestDocument2.EDIT_REASONS_ONLY);
392     }
393     
394     /**
395      * This method tests the PessimisticLockService's ability to establish pessimistic locks for maintenance documents (via maintainables) that
396      * support custom lock descriptors.
397      * 
398      * @throws Exception
399      */
400     @Test
401     public void testPessimisticLockingWithCustomMaintainableLockDescriptors() throws Exception {
402     	MaintenanceDocument maintDoc = (MaintenanceDocument) KRADServiceLocatorWeb.getDocumentService().getNewDocument("AccountType2MaintenanceDocument");
403     	assertTrue("The AccountType2MaintenanceDocument should be using pessimistic locking", KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary(
404 			).getDocumentEntry(maintDoc.getNewMaintainableObject().getDataObjectClass().getSimpleName() + "MaintenanceDocument").getUsePessimisticLocking());
405     	assertTrue("The AccountType2MaintenanceDocument should be using custom lock descriptors", maintDoc.useCustomLockDescriptors());
406     	assertTrue("The AccountType2MaintenanceDocument's new maintainable uses the wrong class",
407     			maintDoc.getNewMaintainableObject() instanceof AccountType2MaintainableImpl);
408     	AccountType2MaintainableImpl newMaint = (AccountType2MaintainableImpl) maintDoc.getNewMaintainableObject();
409     	assertTrue("The AccountType2MaintainableImpl should be using custom lock descriptors", newMaint.useCustomLockDescriptors());
410     	
411     	// Perform the custom lock descriptor unit testing operations.
412        	assertCustomLockDescriptorsAreWorking(maintDoc, AccountType2MaintainableImpl.ACCT_TYPE_2_MAINT_FIELDS_TO_EDIT,
413        			AccountType2MaintainableImpl.EDIT_CODE_ONLY, AccountType2MaintainableImpl.EDIT_NAME_ONLY);
414     }
415     
416     /**
417      * A convenience method for testing the custom lock descriptors of documents (and on the maintainables of maintenance documents).
418      * 
419      * @param testDoc The document to test pessimistic locking on (or the maintenance document with maintainables to test on).
420      * @param LOCK_KEY The UserSession object key to use for storing the lock descriptor's key.
421      * @param LOCK_VALUE1 One possible object to store in a UserSession for generating lock descriptors on the testDoc.
422      * @param LOCK_VALUE2 Another possible object to store in a UserSession for generating lock descriptors on the testDoc.
423      * 
424      * @throws Exception
425      */
426     private void assertCustomLockDescriptorsAreWorking(Document testDoc, final String LOCK_KEY, final Serializable LOCK_VALUE1,
427     		final Serializable LOCK_VALUE2) throws Exception {
428     	PessimisticLockService lockService = KRADServiceLocatorWeb.getPessimisticLockService();
429     	
430     	// Have "quickstart" establish a pessimistic lock on the document by using a custom lock descriptor that only locks part of the document.
431        	UserSession quickstartSession = new UserSession("quickstart");
432        	Person[] allPersons = { quickstartSession.getPerson(), null };
433     	Map<String,String> editMode = new HashMap<String,String>();
434     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
435     	GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE1);
436     	String[] allDescriptors = { testDoc.getCustomLockDescriptor(quickstartSession.getPerson()), null };
437    		assertNotNull("The document should have generated a custom lock descriptor", allDescriptors[0]);
438     	Map <?,?> finalModes = lockService.establishLocks(testDoc, editMode, quickstartSession.getPerson());
439     	
440     	// Verify that the lock was actually established and that the expected custom lock descriptor was used.
441     	assertCorrectLocksAreInPlace(true, finalModes, 1, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
442     	
443     	// Attempt to establish the same lock again, which should change nothing since "quickstart" already has the lock.
444     	editMode = new HashMap<String,String>();
445     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
446     	GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE1);
447     	lockService.establishLocks(testDoc, editMode, quickstartSession.getPerson());
448     	assertCorrectLocksAreInPlace(false, null, 1, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
449     	
450     	// Now check to make sure that a different user (such as "admin") cannot establish a lock using the same lock descriptor.
451     	UserSession adminSession = new UserSession("admin");
452     	editMode = new HashMap<String,String>();
453     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
454        	GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE1);
455     	assertEquals("The document should have generated the same lock descriptors for both 'quickstart' and 'admin'",
456     			allDescriptors[0], testDoc.getCustomLockDescriptor(adminSession.getPerson()));
457     	finalModes = lockService.establishLocks(testDoc, editMode, adminSession.getPerson());
458     	assertCorrectLocksAreInPlace(false, finalModes, 1, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
459     	
460     	// Ensure that "admin" can establish a lock that has a different lock descriptor.
461     	allPersons[1] = adminSession.getPerson();
462     	editMode = new HashMap<String,String>();
463     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
464     	GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE2);
465     	allDescriptors[1] = testDoc.getCustomLockDescriptor(adminSession.getPerson());
466     	assertNotNull("The document should have generated a custom lock descriptor", allDescriptors[1]);
467     	assertNotSame("'quickstart' and 'admin' should have different custom lock descriptors now", allDescriptors[0], allDescriptors[1]);
468     	finalModes = lockService.establishLocks(testDoc, editMode, adminSession.getPerson());
469     	assertCorrectLocksAreInPlace(true, finalModes, 2, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
470     	
471     	// Verify that "quickstart" cannot acquire the lock owned by "admin".
472     	editMode = new HashMap<String,String>();
473     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
474     	GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE2);
475     	lockService.establishLocks(testDoc, editMode, quickstartSession.getPerson());
476     	assertCorrectLocksAreInPlace(false, null, 2, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
477     	
478     	// After "admin" releases his lock, check to make sure that "quickstart" can now acquire it.
479     	lockService.releaseAllLocksForUser(testDoc.getPessimisticLocks(), allPersons[1], allDescriptors[1]);
480     	testDoc.refreshPessimisticLocks();
481     	assertCorrectLocksAreInPlace(false, null, 1, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
482     	allPersons[1] = allPersons[0];
483     	editMode = new HashMap<String,String>();
484     	editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
485     	GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE2);
486     	finalModes = lockService.establishLocks(testDoc, editMode, quickstartSession.getPerson());
487     	assertCorrectLocksAreInPlace(true, finalModes, 2, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
488     	
489     	// Release all the locks when done.
490     	GlobalVariables.getUserSession().removeObject(LOCK_KEY);
491     	lockService.releaseAllLocksForUser(testDoc.getPessimisticLocks(), allPersons[0]);
492     	testDoc.refreshPessimisticLocks();
493     	assertTrue("There should not be any pessimistic locks present on the document", testDoc.getPessimisticLocks().isEmpty());
494     }
495     
496     /**
497      * A convenience method for checking to ensure that the proper pessimistic locks are in place.
498      * 
499      * @param latestUserHasFullEntry Indicates if the map returned by PessimisticLockService.establishLocks should have a true "fullEntry" parameter.
500      * @param finalModes The map returned by the call to PessimisticLockService.establishLocks. This parameter can be null if checking it is not needed.
501      * @param expectedLockQuantity The expected number of pessimistic locks.
502      * @param pessimisticLocks The list of pessimistic locks to check for proper quantity and proper state.
503      * @param expectedOwners The users who are expected to own the corresponding locks in the previous list.
504      * @param expectedDescriptors The expected lock descriptors for the corresponding locks in the other list. This parameter can be set to null if
505      * the pessimistic locks are not using custom lock descriptors or if custom lock descriptors are not the concern of the test.
506      * @throws Exception
507      */
508     private void assertCorrectLocksAreInPlace(boolean latestUserHasFullEntry, Map<?,?> finalModes, int expectedLockQuantity,
509     		List<PessimisticLock> pessimisticLocks, Person[] expectedOwners, String[] expectedDescriptors) throws Exception {
510     	// Ensure that the last user to attempt to establish locks has the expected finalModes entry (or lack of it).
511     	if (finalModes != null) {
512     		assertEquals("The last user that tried to establish locks does not have the expected status on their full entry privileges",
513     				latestUserHasFullEntry, StringUtils.equalsIgnoreCase(KRADConstants.KUALI_DEFAULT_TRUE_VALUE, (String)(finalModes.get(
514                     AuthorizationConstants.EditMode.FULL_ENTRY))));
515     	}
516     	// Ensure that the expected number of locks are present.
517     	assertEquals("The wrong number of pessimistic locks are in place", expectedLockQuantity, pessimisticLocks.size());
518     	// Verify that each lock has the expected owners.
519     	for (int i = pessimisticLocks.size() - 1; i > -1; i--) {
520     		assertTrue("The lock at index " + i + " did not have the expected owner of " + expectedOwners[i].getPrincipalName(),
521     			pessimisticLocks.get(i).isOwnedByUser(expectedOwners[i]));
522     		if (expectedDescriptors != null) {
523     			assertTrue("The lock at index " + i + " did not have the expected lock descriptor of " + expectedDescriptors[i],
524     					pessimisticLocks.get(i).getLockDescriptor().equals(expectedDescriptors[i]));
525     		}
526     	}
527     }
528 }