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