001/**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.service;
017
018import org.apache.commons.lang.StringUtils;
019import org.junit.Test;
020import org.kuali.rice.kew.api.exception.WorkflowException;
021import org.kuali.rice.kim.api.KimConstants.PermissionNames;
022import org.kuali.rice.kim.api.identity.Person;
023import org.kuali.rice.kim.api.services.KimApiServiceLocator;
024import org.kuali.rice.kns.authorization.AuthorizationConstants;
025import org.kuali.rice.krad.UserSession;
026import org.kuali.rice.krad.document.Document;
027import org.kuali.rice.krad.maintenance.MaintenanceDocument;
028import org.kuali.rice.krad.document.authorization.PessimisticLock;
029import org.kuali.rice.krad.exception.AuthorizationException;
030import org.kuali.rice.krad.service.impl.PessimisticLockServiceImpl;
031import org.kuali.rice.krad.test.document.AccountRequestDocument;
032import org.kuali.rice.krad.test.document.AccountRequestDocument2;
033import org.kuali.rice.krad.util.GlobalVariables;
034import org.kuali.rice.krad.util.KRADConstants;
035import org.kuali.rice.krad.util.KRADPropertyConstants;
036import org.kuali.rice.test.BaselineTestCase;
037import org.kuali.rice.test.data.UnitTestData;
038import org.kuali.rice.test.data.UnitTestSql;
039import org.kuali.rice.krad.test.KRADTestCase;
040
041import java.io.Serializable;
042import java.util.ArrayList;
043import java.util.Arrays;
044import java.util.Collections;
045import java.util.HashMap;
046import java.util.HashSet;
047import java.util.List;
048import java.util.Map;
049import java.util.Set;
050
051import static org.junit.Assert.*;
052
053
054/**
055 * PessimisticLockServiceTest tests {@link PessimisticLockServiceImpl}
056 *
057 * @author Kuali Rice Team (rice.collab@kuali.org)
058 */
059@BaselineTestCase.BaselineMode(BaselineTestCase.Mode.NONE)
060public class PessimisticLockServiceTest extends KRADTestCase {
061
062    String sessionId = "ad4d6c83-4d0f-4309-a528-c2f81ec00396";
063
064    @Override
065    public void setUp() throws Exception {
066        super.setUp();
067        GlobalVariables.setUserSession(new UserSession("quickstart"));
068        GlobalVariables.getUserSession().setKualiSessionId(sessionId);
069    }
070
071    /**
072     * tests deleting {@link PessimisticLock} objects. Tests that invalid deletes throw exceptions and valid
073     * deletes by owner users as well as lock admin users do work as expected
074     *
075     * @throws Exception
076     */
077    @UnitTestData(
078            sqlStatements = {
079                    @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
080                    @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')"),
081                    @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')"),
082                    @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')"),
083                    @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')")
084                    }
085            )
086    @Test
087    public void testDeleteLocks() throws Exception {
088        
089        List<PessimisticLock> locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
090        assertEquals("Should be 4 locks in DB", 4, locks.size());
091
092        String userId = "employee";
093        String[] lockIdsToVerify = new String[]{"1112", "1113"};
094        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,
095                Collections.<String, String>emptyMap() ) );
096        verifyDelete(userId, Arrays.asList(lockIdsToVerify), AuthorizationException.class, true);
097        userId = "frank";
098        lockIdsToVerify = new String[]{"1111", "1113"};
099        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}