001/**
002 * Copyright 2005-2011 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.maintainable.AccountType2MaintainableImpl;
037import org.kuali.rice.test.BaselineTestCase;
038import org.kuali.rice.test.data.UnitTestData;
039import org.kuali.rice.test.data.UnitTestSql;
040import org.kuali.test.KRADTestCase;
041
042import java.io.Serializable;
043import java.util.ArrayList;
044import java.util.Arrays;
045import java.util.Collections;
046import java.util.HashMap;
047import java.util.HashSet;
048import java.util.List;
049import java.util.Map;
050import java.util.Set;
051
052import static org.junit.Assert.*;
053
054
055/**
056 * This class is used to test the {@link PessimisticLockServiceImpl} class
057 *
058 * @author Kuali Rice Team (rice.collab@kuali.org)
059 */
060@BaselineTestCase.BaselineMode(BaselineTestCase.Mode.NONE)
061public class PessimisticLockServiceTest extends KRADTestCase {
062
063    String sessionId = "ad4d6c83-4d0f-4309-a528-c2f81ec00396";
064
065    @Override
066    public void setUp() throws Exception {
067        super.setUp();
068        GlobalVariables.setUserSession(new UserSession("quickstart"));
069        GlobalVariables.getUserSession().setKualiSessionId(sessionId);
070    }
071
072    /**
073     * This method tests deleting {@link PessimisticLock} objects. Tests that invalid deletes throw exceptions and valid
074     * deletes by owner users as well as lock admin users do work as expected
075     *
076     * @throws Exception
077     */
078    @UnitTestData(
079            sqlStatements = {
080                    @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
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 (1111, '4f6bc9e2-7df8-102c-97b6-ed716fdaf540', 0, NULL, '1234', {d '2007-07-01'}, 'employee', 'aa5d6c83-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 (1112, '5add9cba-7df8-102c-97b6-ed716fdaf540', 0, NULL, '1235', {d '2007-10-01'}, 'frank', 'dd4d6c83-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 (1113, '69e42b8e-7df8-102c-97b6-ed716fdaf540', 0, NULL, '1236', {d '2007-08-01'}, 'fred', 'ad4d6c83-4d0f-4309-a528-c2f81ec00396')"),
084                    @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')")
085                    }
086            )
087    @Test
088    public void testDeleteLocks() throws Exception {
089        
090        List<PessimisticLock> locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
091        assertEquals("Should be 4 locks in DB", 4, locks.size());
092
093        String userId = "employee";
094        String[] lockIdsToVerify = new String[]{"1112", "1113"};
095        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,
096                Collections.<String, String>emptyMap() ) );
097        verifyDelete(userId, Arrays.asList(lockIdsToVerify), AuthorizationException.class, true);
098        userId = "frank";
099        lockIdsToVerify = new String[]{"1111", "1113"};
100        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() ) );
101        verifyDelete(userId, Arrays.asList(lockIdsToVerify), AuthorizationException.class, true);
102        userId = "fred";
103        lockIdsToVerify = new String[]{"1111", "1112"};
104        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() ) );
105        verifyDelete(userId, Arrays.asList(lockIdsToVerify), AuthorizationException.class, true);
106
107        verifyDelete("employee", Arrays.asList(new String[]{"1111"}), null, false);
108        verifyDelete("frank", Arrays.asList(new String[]{"1112"}), null, false);
109        verifyDelete("fred", Arrays.asList(new String[]{"1113"}), null, false);
110        locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
111        assertEquals("Should be 1 lock left in DB", 1, locks.size());
112
113        // test admin user can delete any lock
114        userId = "fran";
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        userId = "admin";
117        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() ) );
118        verifyDelete(userId, Arrays.asList(new String[]{"1114"}), null, false);
119        locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
120        assertEquals("Should be 0 locks left in DB", 0, locks.size());
121    }
122
123    private void verifyDelete(String userId, List<String> lockIds, Class expectedException, boolean expectException) throws WorkflowException {
124        GlobalVariables.setUserSession(new UserSession(userId));
125        for (String lockId : lockIds) {
126            try {
127                KRADServiceLocatorWeb.getPessimisticLockService().delete(lockId);
128                if (expectException) {
129                    fail("Expected exception when deleting lock with id '" + lockId + "' for user '" + userId + "'");
130                }
131            } catch (Exception e) {
132                if (!expectException) {
133                    fail("Did not expect exception when deleting lock with id '" + lockId + "' for user '" + userId + "' but got exception of type '" + e.getClass().getName() + "'");
134                }
135                if (expectedException != null) {
136                    // if we have an expected exception
137                    if (!expectedException.isAssignableFrom(e.getClass())) {
138                        fail("Expected exception of type '" + expectedException.getName() + "' when deleting lock with id '" + lockId + "' for user '" + userId + "' but got exception of type '" + e.getClass().getName() + "'");
139                    }
140                }
141            }
142        }
143    }
144
145    /**
146     * This method tests the generation of new {@link PessimisticLock} objects
147     *
148     * @throws Exception
149     */
150    @Test
151    public void testGenerateNewLocks() throws Exception {
152        PessimisticLockService lockService = KRADServiceLocatorWeb.getPessimisticLockService();
153
154        // test generating lock with no given lock descriptor
155        String documentNumber = "1243";
156        PessimisticLock lock = lockService.generateNewLock(documentNumber);
157        assertNotNull("Generated lock should have id", lock.getId());
158        assertEquals("Document Number should match", documentNumber, lock.getDocumentNumber());
159        assertNotNull("Generated lock should have a generated timestamp ", lock.getGeneratedTimestamp());
160        assertEquals("Generated lock should have default lock descriptor", PessimisticLock.DEFAULT_LOCK_DESCRIPTOR, lock.getLockDescriptor());
161        assertEquals("Generated lock should be owned by current user", GlobalVariables.getUserSession().getPerson().getPrincipalName(), lock.getOwnedByUser().getPrincipalName());
162        Map primaryKeys = new HashMap();
163        primaryKeys.put(KRADPropertyConstants.ID, lock.getId());
164        lock = null;
165        lock = (PessimisticLock) KRADServiceLocator.getBusinessObjectService().findByPrimaryKey(PessimisticLock.class, primaryKeys);
166        assertNotNull("Generated lock should be available from BO Service", lock);
167        assertNotNull("Generated lock should have id", lock.getId());
168        assertEquals("Document Number should match", documentNumber, lock.getDocumentNumber());
169        assertNotNull("Generated lock should have a generated timestamp ", lock.getGeneratedTimestamp());
170        assertEquals("Generated lock should have default lock descriptor", PessimisticLock.DEFAULT_LOCK_DESCRIPTOR, lock.getLockDescriptor());
171        assertEquals("Generated lock should be owned by current user", GlobalVariables.getUserSession().getPerson().getPrincipalName(), lock.getOwnedByUser().getPrincipalName());
172
173        // test generating lock with given lock descriptor
174        lock = null;
175        documentNumber = "4321";
176        String lockDescriptor = "this is a test lock descriptor";
177        lock = lockService.generateNewLock(documentNumber, lockDescriptor);
178        assertNotNull("Generated lock should have id", lock.getId());
179        assertEquals("Document Number should match", documentNumber, lock.getDocumentNumber());
180        assertNotNull("Generated lock should have a generated timestamp ", lock.getGeneratedTimestamp());
181        assertEquals("Generated lock should have lock descriptor set", lockDescriptor, lock.getLockDescriptor());
182        assertEquals("Generated lock should be owned by current user", GlobalVariables.getUserSession().getPerson().getPrincipalName(), lock.getOwnedByUser().getPrincipalName());
183        primaryKeys = new HashMap();
184        primaryKeys.put(KRADPropertyConstants.ID, lock.getId());
185        lock = null;
186        lock = (PessimisticLock) KRADServiceLocator.getBusinessObjectService().findByPrimaryKey(PessimisticLock.class, primaryKeys);
187        assertNotNull("Generated lock should be available from BO Service", lock);
188        assertNotNull("Generated lock should have id", lock.getId());
189        assertEquals("Document Number should match", documentNumber, lock.getDocumentNumber());
190        assertNotNull("Generated lock should have a generated timestamp ", lock.getGeneratedTimestamp());
191        assertEquals("Generated lock should have lock descriptor set", lockDescriptor, lock.getLockDescriptor());
192        assertEquals("Generated lock should be owned by current user", GlobalVariables.getUserSession().getPerson().getPrincipalName(), lock.getOwnedByUser().getPrincipalName());
193    }
194
195    /**
196     * This method tests retrieving {@link PessimisticLock} objects by document number
197     *
198     * @throws Exception
199     */
200    @UnitTestData(
201            sqlStatements = {
202                    @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
203                    @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')"),
204                    @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')"),
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 (1113, '0e0144ec-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1236', {d '2007-10-01'}, 'frank', 'dd5d6c83-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 (1114, '1891526c-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1237', {d '2007-08-01'}, 'fred', 'ab4d6c83-4d0f-4309-a528-c2f81ec00396')")
207                    }
208            )
209    @Test
210    public void testGetPessimisticLocksForDocument() throws Exception {
211        PessimisticLockService lockService = KRADServiceLocatorWeb.getPessimisticLockService();
212        String docId = "1234";
213        assertEquals("Document " + docId + " expected lock count incorrect", 1, lockService.getPessimisticLocksForDocument(docId).size());
214        docId = "1237";
215        assertEquals("Document " + docId + " expected lock count incorrect", 2, lockService.getPessimisticLocksForDocument(docId).size());
216        docId = "1236";
217        assertEquals("Document " + docId + " expected lock count incorrect", 1, lockService.getPessimisticLocksForDocument(docId).size());
218        docId = "3948";
219        assertEquals("Document " + docId + " expected lock count incorrect", 0, lockService.getPessimisticLocksForDocument(docId).size());
220    }
221
222    /**
223     * This method tests retrieving {@link PessimisticLock} objects by session id
224     *
225     * @throws Exception
226     */
227    @UnitTestData(
228            sqlStatements = {
229                    @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
230                    @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID,SESN_ID) VALUES (1111, '24c40cd2-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1234', {d '2007-07-01'}, 'fran', 'ad4d6c83-4d0f-4309-a528-c2f81ec00396')"),
231                    @UnitTestSql("INSERT INTO KRNS_PESSIMISTIC_LOCK_T (PESSIMISTIC_LOCK_ID,OBJ_ID,VER_NBR,LOCK_DESC_TXT,DOC_HDR_ID,GNRT_DT,PRNCPL_ID,SESN_ID) VALUES (1112, '32602e8e-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1235', {d '2007-10-01'}, 'fran', 'bc5d6c66-4d0f-4309-a528-c2f81ec00396')"),
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 (1113, '3acfc1ce-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1236', {d '2007-10-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 (1114, '463cc642-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1237', {d '2007-08-01'}, 'fran', 'bc5d6c66-4d0f-4309-a528-c2f81ec00396')")
234            }
235    )
236    @Test
237    public void testGetPessimisticLocksForSession() throws Exception {
238        List<PessimisticLock> locks = KRADServiceLocatorWeb.getPessimisticLockService().getPessimisticLocksForSession(sessionId);
239        assertEquals("Should return 2 locks for session " + sessionId, 2, locks.size());
240
241        ArrayList<String> documentNumbers = new ArrayList<String>();
242        for (PessimisticLock lock : locks) {
243            documentNumbers.add(lock.getDocumentNumber());
244        }
245        assertTrue("Locks should contain a lock for document number 1234 but contained " + documentNumbers, documentNumbers.contains("1234"));
246        assertTrue("Locks should contain a lock for document number 1236 but contained " + documentNumbers, documentNumbers.contains("1236"));
247    }
248
249    /**
250     * This method tests releasing {@link PessimisticLock} objects for a specific user
251     *
252     * @throws Exception
253     */
254    @UnitTestData(
255            sqlStatements = {
256                    @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
257                    @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')"),
258                    @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')"),
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 (1113, '3acfc1ce-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1236', {d '2007-10-01'}, 'frank', 'bc5d6c66-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 (1114, '463cc642-7dfc-102c-97b6-ed716fdaf540', 0, NULL, '1237', {d '2007-08-01'}, 'fred', 'dd5d6c66-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 (1115, '4e66c4b2-7dfc-102c-97b6-ed716fdaf540', 0, 'Temporary Lock', '1234', {d '2007-07-01'}, 'fran', 'ad4d6c83-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 (1116, '55d99b02-7dfc-102c-97b6-ed716fdaf540', 0, 'Temporary Lock', '1235', {d '2007-10-01'}, 'frank', 'bc5d6c66-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 (1117, '5e47fb26-7dfc-102c-97b6-ed716fdaf540', 0, 'Temporary Lock', '1236', {d '2007-10-01'}, 'frank', 'bc5d6c66-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 (1118, '65c366d8-7dfc-102c-97b6-ed716fdaf540', 0, 'Temporary Lock', '1237', {d '2007-08-01'}, 'fred', 'dd5d6c66-4d0f-4309-a528-c2f81ec00396')")
265                    }
266            )
267    @Test
268    public void testReleaseAllLocksForUser() throws Exception {
269        String lockDescriptor = "Temporary Lock";
270        List<PessimisticLock> locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
271        assertEquals("Should be 8 manually inserted locks", 8, locks.size());
272
273        KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("fran"), lockDescriptor);
274        locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
275        assertEquals("Should be 7 locks left after releasing locks for fran using lock descriptor " + lockDescriptor, 7, locks.size());
276
277        KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("frank"), lockDescriptor);
278        locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
279        assertEquals("Should be 5 locks left after releasing locks for fran and frank using lock descriptor " + lockDescriptor, 5, locks.size());
280
281        KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("fred"), lockDescriptor);
282        locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
283        assertEquals("Should be 4 locks left after releasing locks for fran, frank, and fred using lock descriptor " + lockDescriptor, 4, locks.size());
284
285        KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("fran"));
286        locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
287        assertEquals("Should be 3 locks left after releasing locks for fran with no lock descriptor", 3, locks.size());
288
289        KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("frank"));
290        locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
291        assertEquals("Should be 1 lock left after releasing locks for fran and frank with no lock descriptor", 1, locks.size());
292
293        KRADServiceLocatorWeb.getPessimisticLockService().releaseAllLocksForUser(locks, KimApiServiceLocator.getPersonService().getPerson("fred"));
294        locks = (List<PessimisticLock>) KRADServiceLocator.getBusinessObjectService().findAll(PessimisticLock.class);
295        assertEquals("Should be no locks left after releasing locks for fran, frank, and fred with no lock descriptor", 0, locks.size());
296    }
297
298    /**
299     * This method tests saving {@link PessimisticLock} objects
300     *
301     * @throws Exception
302     */
303    @UnitTestData(
304            sqlStatements = {
305                    @UnitTestSql("DELETE FROM KRNS_PESSIMISTIC_LOCK_T"),
306                    @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')")
307                    }
308            )
309    @Test
310    public void testSaveLock() throws Exception {
311        String lockDescriptor = "new test lock descriptor";
312        // get existing lock and update lock descriptor and save
313        Map primaryKeys = new HashMap();
314        primaryKeys.put(KRADPropertyConstants.ID, Long.valueOf("1111"));
315        PessimisticLock lock = (PessimisticLock) KRADServiceLocator.getBusinessObjectService().findByPrimaryKey(PessimisticLock.class, primaryKeys);
316        lock.setLockDescriptor(lockDescriptor);
317        KRADServiceLocatorWeb.getPessimisticLockService().save(lock);
318
319        // verify retrieved lock has lock descriptor set previously
320        PessimisticLock savedLock = (PessimisticLock) KRADServiceLocator.getBusinessObjectService().findByPrimaryKey(PessimisticLock.class, primaryKeys);
321        assertEquals("Lock descriptor is not correct from lock that was saved", lockDescriptor, savedLock.getLockDescriptor());
322    }
323    
324    /**
325     * This method 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     * This method 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     * This method 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     * This method tests the PessimisticLockService's ability to establish pessimistic locks for maintenance documents (via maintainables) that
427     * support custom lock descriptors.
428     * 
429     * @throws Exception
430     */
431    @Test
432    public void testPessimisticLockingWithCustomMaintainableLockDescriptors() throws Exception {
433        MaintenanceDocument maintDoc = (MaintenanceDocument) KRADServiceLocatorWeb.getDocumentService().getNewDocument("AccountType2MaintenanceDocument");
434        assertTrue("The AccountType2MaintenanceDocument should be using pessimistic locking", KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary(
435                        ).getDocumentEntry(maintDoc.getNewMaintainableObject().getDataObjectClass().getSimpleName() + "MaintenanceDocument").getUsePessimisticLocking());
436        assertTrue("The AccountType2MaintenanceDocument should be using custom lock descriptors", maintDoc.useCustomLockDescriptors());
437        assertTrue("The AccountType2MaintenanceDocument's new maintainable uses the wrong class",
438                        maintDoc.getNewMaintainableObject() instanceof AccountType2MaintainableImpl);
439        AccountType2MaintainableImpl newMaint = (AccountType2MaintainableImpl) maintDoc.getNewMaintainableObject();
440        assertTrue("The AccountType2MaintainableImpl should be using custom lock descriptors", newMaint.useCustomLockDescriptors());
441        
442        // Perform the custom lock descriptor unit testing operations.
443        assertCustomLockDescriptorsAreWorking(maintDoc, AccountType2MaintainableImpl.ACCT_TYPE_2_MAINT_FIELDS_TO_EDIT,
444                        AccountType2MaintainableImpl.EDIT_CODE_ONLY, AccountType2MaintainableImpl.EDIT_NAME_ONLY);
445    }
446    
447    /**
448     * A convenience method for testing the custom lock descriptors of documents (and on the maintainables of maintenance documents).
449     * 
450     * @param testDoc The document to test pessimistic locking on (or the maintenance document with maintainables to test on).
451     * @param LOCK_KEY The UserSession object key to use for storing the lock descriptor's key.
452     * @param LOCK_VALUE1 One possible object to store in a UserSession for generating lock descriptors on the testDoc.
453     * @param LOCK_VALUE2 Another possible object to store in a UserSession for generating lock descriptors on the testDoc.
454     * 
455     * @throws Exception
456     */
457    private void assertCustomLockDescriptorsAreWorking(Document testDoc, final String LOCK_KEY, final Serializable LOCK_VALUE1,
458                final Serializable LOCK_VALUE2) throws Exception {
459        PessimisticLockService lockService = KRADServiceLocatorWeb.getPessimisticLockService();
460        
461        // Have "quickstart" establish a pessimistic lock on the document by using a custom lock descriptor that only locks part of the document.
462        UserSession quickstartSession = new UserSession("quickstart");
463        Person[] allPersons = { quickstartSession.getPerson(), null };
464        Map<String,String> editMode = new HashMap<String,String>();
465        editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
466        GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE1);
467        String[] allDescriptors = { testDoc.getCustomLockDescriptor(quickstartSession.getPerson()), null };
468                assertNotNull("The document should have generated a custom lock descriptor", allDescriptors[0]);
469        Map <?,?> finalModes = lockService.establishLocks(testDoc, editMode, quickstartSession.getPerson());
470        
471        // Verify that the lock was actually established and that the expected custom lock descriptor was used.
472        assertCorrectLocksAreInPlace(true, finalModes, 1, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
473        
474        // Attempt to establish the same lock again, which should change nothing since "quickstart" already has the lock.
475        editMode = new HashMap<String,String>();
476        editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
477        GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE1);
478        lockService.establishLocks(testDoc, editMode, quickstartSession.getPerson());
479        assertCorrectLocksAreInPlace(false, null, 1, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
480        
481        // Now check to make sure that a different user (such as "admin") cannot establish a lock using the same lock descriptor.
482        UserSession adminSession = new UserSession("admin");
483        editMode = new HashMap<String,String>();
484        editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
485        GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE1);
486        assertEquals("The document should have generated the same lock descriptors for both 'quickstart' and 'admin'",
487                        allDescriptors[0], testDoc.getCustomLockDescriptor(adminSession.getPerson()));
488        finalModes = lockService.establishLocks(testDoc, editMode, adminSession.getPerson());
489        assertCorrectLocksAreInPlace(false, finalModes, 1, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
490        
491        // Ensure that "admin" can establish a lock that has a different lock descriptor.
492        allPersons[1] = adminSession.getPerson();
493        editMode = new HashMap<String,String>();
494        editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
495        GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE2);
496        allDescriptors[1] = testDoc.getCustomLockDescriptor(adminSession.getPerson());
497        assertNotNull("The document should have generated a custom lock descriptor", allDescriptors[1]);
498        assertNotSame("'quickstart' and 'admin' should have different custom lock descriptors now", allDescriptors[0], allDescriptors[1]);
499        finalModes = lockService.establishLocks(testDoc, editMode, adminSession.getPerson());
500        assertCorrectLocksAreInPlace(true, finalModes, 2, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
501        
502        // Verify that "quickstart" cannot acquire the lock owned by "admin".
503        editMode = new HashMap<String,String>();
504        editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
505        GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE2);
506        lockService.establishLocks(testDoc, editMode, quickstartSession.getPerson());
507        assertCorrectLocksAreInPlace(false, null, 2, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
508        
509        // After "admin" releases his lock, check to make sure that "quickstart" can now acquire it.
510        lockService.releaseAllLocksForUser(testDoc.getPessimisticLocks(), allPersons[1], allDescriptors[1]);
511        testDoc.refreshPessimisticLocks();
512        assertCorrectLocksAreInPlace(false, null, 1, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
513        allPersons[1] = allPersons[0];
514        editMode = new HashMap<String,String>();
515        editMode.put(AuthorizationConstants.EditMode.FULL_ENTRY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
516        GlobalVariables.getUserSession().addObject(LOCK_KEY, LOCK_VALUE2);
517        finalModes = lockService.establishLocks(testDoc, editMode, quickstartSession.getPerson());
518        assertCorrectLocksAreInPlace(true, finalModes, 2, testDoc.getPessimisticLocks(), allPersons, allDescriptors);
519        
520        // Release all the locks when done.
521        GlobalVariables.getUserSession().removeObject(LOCK_KEY);
522        lockService.releaseAllLocksForUser(testDoc.getPessimisticLocks(), allPersons[0]);
523        testDoc.refreshPessimisticLocks();
524        assertTrue("There should not be any pessimistic locks present on the document", testDoc.getPessimisticLocks().isEmpty());
525    }
526    
527    /**
528     * A convenience method for checking to ensure that the proper pessimistic locks are in place.
529     * 
530     * @param latestUserHasFullEntry Indicates if the map returned by PessimisticLockService.establishLocks should have a true "fullEntry" parameter.
531     * @param finalModes The map returned by the call to PessimisticLockService.establishLocks. This parameter can be null if checking it is not needed.
532     * @param expectedLockQuantity The expected number of pessimistic locks.
533     * @param pessimisticLocks The list of pessimistic locks to check for proper quantity and proper state.
534     * @param expectedOwners The users who are expected to own the corresponding locks in the previous list.
535     * @param expectedDescriptors The expected lock descriptors for the corresponding locks in the other list. This parameter can be set to null if
536     * the pessimistic locks are not using custom lock descriptors or if custom lock descriptors are not the concern of the test.
537     * @throws Exception
538     */
539    private void assertCorrectLocksAreInPlace(boolean latestUserHasFullEntry, Map<?,?> finalModes, int expectedLockQuantity,
540                List<PessimisticLock> pessimisticLocks, Person[] expectedOwners, String[] expectedDescriptors) throws Exception {
541        // Ensure that the last user to attempt to establish locks has the expected finalModes entry (or lack of it).
542        if (finalModes != null) {
543                assertEquals("The last user that tried to establish locks does not have the expected status on their full entry privileges",
544                                latestUserHasFullEntry, StringUtils.equalsIgnoreCase(KRADConstants.KUALI_DEFAULT_TRUE_VALUE, (String)(finalModes.get(
545                    AuthorizationConstants.EditMode.FULL_ENTRY))));
546        }
547        // Ensure that the expected number of locks are present.
548        assertEquals("The wrong number of pessimistic locks are in place", expectedLockQuantity, pessimisticLocks.size());
549        // Verify that each lock has the expected owners.
550        for (int i = pessimisticLocks.size() - 1; i > -1; i--) {
551                assertTrue("The lock at index " + i + " did not have the expected owner of " + expectedOwners[i].getPrincipalName(),
552                        pessimisticLocks.get(i).isOwnedByUser(expectedOwners[i]));
553                if (expectedDescriptors != null) {
554                        assertTrue("The lock at index " + i + " did not have the expected lock descriptor of " + expectedDescriptors[i],
555                                        pessimisticLocks.get(i).getLockDescriptor().equals(expectedDescriptors[i]));
556                }
557        }
558    }
559}