View Javadoc

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