001/**
002 * Copyright 2005-2016 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.impl;
017
018import java.util.ArrayList;
019import java.util.Collections;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026
027import org.apache.commons.lang.StringUtils;
028import org.kuali.rice.core.api.criteria.QueryByCriteria;
029import org.kuali.rice.core.api.util.RiceConstants;
030import org.kuali.rice.kim.api.KimConstants.PermissionNames;
031import org.kuali.rice.kim.api.identity.Person;
032import org.kuali.rice.kim.api.identity.PersonService;
033import org.kuali.rice.kim.api.permission.PermissionService;
034import org.kuali.rice.kim.api.services.KimApiServiceLocator;
035import org.kuali.rice.kns.authorization.AuthorizationConstants;
036import org.kuali.rice.krad.data.DataObjectService;
037import org.kuali.rice.krad.document.Document;
038import org.kuali.rice.krad.document.authorization.PessimisticLock;
039import org.kuali.rice.krad.exception.AuthorizationException;
040import org.kuali.rice.krad.exception.PessimisticLockingException;
041import org.kuali.rice.krad.service.DataDictionaryService;
042import org.kuali.rice.krad.service.LegacyDataAdapter;
043import org.kuali.rice.krad.service.PessimisticLockService;
044import org.kuali.rice.krad.util.GlobalVariables;
045import org.kuali.rice.krad.util.KRADConstants;
046import org.kuali.rice.krad.util.KRADPropertyConstants;
047import org.springframework.beans.factory.annotation.Required;
048import org.springframework.transaction.annotation.Transactional;
049
050/**
051 * Service implementation for pessimistic locking
052 *
053 * @author Kuali Rice Team (rice.collab@kuali.org)
054 */
055@Transactional
056public class PessimisticLockServiceImpl implements PessimisticLockService {
057    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PessimisticLockServiceImpl.class);
058
059    protected DataObjectService dataObjectService;
060    protected DataDictionaryService dataDictionaryService;
061    protected PermissionService permissionService;
062    protected PersonService personService;
063
064        /**
065     * @see org.kuali.rice.krad.service.PessimisticLockService#delete(java.lang.String)
066     */
067    @Override
068        public void delete(String id) {
069        if (StringUtils.isBlank(id)) {
070            throw new IllegalArgumentException("An invalid blank id was passed to delete a Pessimistic Lock.");
071        }
072        PessimisticLock lock = dataObjectService.find(PessimisticLock.class, Long.valueOf(id));
073        if ( lock == null ) {
074            throw new IllegalArgumentException("Pessimistic Lock with id " + id + " cannot be found in the database.");
075        }
076        Person user = GlobalVariables.getUserSession().getPerson();
077        if ( (!lock.isOwnedByUser(user)) && (!isPessimisticLockAdminUser(user)) ) {
078            throw new AuthorizationException(user.getName(),"delete", "Pessimistick Lock (id " + id + ")");
079        }
080        delete(lock);
081    }
082
083    private void delete(PessimisticLock lock) {
084        if ( LOG.isDebugEnabled() ) {
085                LOG.debug("Deleting lock: " + lock);
086        }
087        dataObjectService.delete(lock);
088    }
089
090    /**
091     * @see org.kuali.rice.krad.service.PessimisticLockService#generateNewLock(String)
092     */
093    @Override
094        public PessimisticLock generateNewLock(String documentNumber) {
095        return generateNewLock(documentNumber, GlobalVariables.getUserSession().getPerson());
096    }
097
098    /**
099     * @see org.kuali.rice.krad.service.PessimisticLockService#generateNewLock(java.lang.String)
100     */
101    @Override
102        public PessimisticLock generateNewLock(String documentNumber, String lockDescriptor) {
103        return generateNewLock(documentNumber, lockDescriptor, GlobalVariables.getUserSession().getPerson());
104    }
105
106    /**
107     * @see org.kuali.rice.krad.service.PessimisticLockService#generateNewLock(java.lang.String, org.kuali.rice.kim.api.identity.Person)
108     */
109    @Override
110        public PessimisticLock generateNewLock(String documentNumber, Person user) {
111        return generateNewLock(documentNumber, PessimisticLock.DEFAULT_LOCK_DESCRIPTOR, user);
112    }
113
114    /**
115     * @see org.kuali.rice.krad.service.PessimisticLockService#generateNewLock(java.lang.String, java.lang.String, org.kuali.rice.kim.api.identity.Person)
116     */
117    @Override
118        public PessimisticLock generateNewLock(String documentNumber, String lockDescriptor, Person user) {
119        PessimisticLock lock = new PessimisticLock(documentNumber, lockDescriptor, user, GlobalVariables.getUserSession());
120        lock = save(lock);
121        if ( LOG.isDebugEnabled() ) {
122                LOG.debug("Generated new lock: " + lock);
123        }
124        return lock;
125    }
126
127    /**
128     * @see org.kuali.rice.krad.service.PessimisticLockService#getPessimisticLocksForDocument(java.lang.String)
129     */
130    @Override
131        public List<PessimisticLock> getPessimisticLocksForDocument(String documentNumber) {
132        return new ArrayList<PessimisticLock>( dataObjectService.findMatching(PessimisticLock.class,
133                        QueryByCriteria.Builder.forAttribute(KRADPropertyConstants.DOCUMENT_NUMBER, documentNumber)
134                        .build()).getResults() );
135    }
136
137    /**
138     * @see org.kuali.rice.krad.service.PessimisticLockService#getPessimisticLocksForSession(java.lang.String)
139     */
140    @Override
141        public List<PessimisticLock> getPessimisticLocksForSession(String sessionId) {
142        return new ArrayList<PessimisticLock>( dataObjectService.findMatching(PessimisticLock.class,
143                        QueryByCriteria.Builder.forAttribute(KRADPropertyConstants.SESSION_ID, sessionId)
144                        .build()).getResults() );
145    }
146
147    /**
148     * @see org.kuali.rice.krad.service.PessimisticLockService#isPessimisticLockAdminUser(org.kuali.rice.kim.api.identity.Person)
149     */
150    @Override
151        public boolean isPessimisticLockAdminUser(Person user) {
152        return getPermissionService().isAuthorized( user.getPrincipalId(), KRADConstants.KNS_NAMESPACE, PermissionNames.ADMIN_PESSIMISTIC_LOCKING,
153                Collections.<String, String>emptyMap() );
154    }
155
156    /**
157     * @see org.kuali.rice.krad.service.PessimisticLockService#releaseAllLocksForUser(java.util.List, org.kuali.rice.kim.api.identity.Person)
158     */
159    @Override
160        public void releaseAllLocksForUser(List<PessimisticLock> locks, Person user) {
161        for (Iterator<PessimisticLock> iterator = locks.iterator(); iterator.hasNext();) {
162            PessimisticLock lock = iterator.next();
163            if (lock.isOwnedByUser(user)) {
164                try {
165                    delete(lock);
166                } catch ( RuntimeException ex ) {
167                        if ( ex.getCause() != null && ex.getCause().getClass().equals( LegacyDataAdapter.OPTIMISTIC_LOCK_OJB_EXCEPTION_CLASS ) ) {
168                        LOG.warn( "Suppressing Optimistic Lock Exception. Document Num: " +  lock.getDocumentNumber());
169                    } else {
170                        throw ex;
171                    }
172                }
173            }
174        }
175    }
176
177    /**
178     * @see org.kuali.rice.krad.service.PessimisticLockService#releaseAllLocksForUser(java.util.List, org.kuali.rice.kim.api.identity.Person, java.lang.String)
179     */
180    @Override
181        public void releaseAllLocksForUser(List<PessimisticLock> locks, Person user, String lockDescriptor) {
182        for (Iterator<PessimisticLock> iterator = locks.iterator(); iterator.hasNext();) {
183            PessimisticLock lock = iterator.next();
184            if ( (lock.isOwnedByUser(user)) && (lockDescriptor.equals(lock.getLockDescriptor())) ) {
185                try {
186                    delete(lock);
187                } catch ( RuntimeException ex ) {
188                        if ( ex.getCause() != null && ex.getCause().getClass().equals( LegacyDataAdapter.OPTIMISTIC_LOCK_OJB_EXCEPTION_CLASS ) ) {
189                        LOG.warn( "Suppressing Optimistic Lock Exception. Document Num: " +  lock.getDocumentNumber());
190                    } else {
191                        throw ex;
192                    }
193                }
194            }
195        }
196    }
197
198    /**
199     * @see org.kuali.rice.krad.service.PessimisticLockService#save(org.kuali.rice.krad.document.authorization.PessimisticLock)
200     */
201    @Override
202        public PessimisticLock save(PessimisticLock lock) {
203        if ( LOG.isDebugEnabled() ) {
204                LOG.debug("Saving lock: " + lock);
205        }
206        return dataObjectService.save(lock);
207    }
208
209    /**
210     * {@inheritDoc}
211     */
212    @Override
213    public boolean establishPessimisticLocks(Document document, Person user, boolean canEdit) {
214        // establish pessimistic locks if needed
215        if (isPessimisticLockNeeded(document, user, canEdit)) {
216            PessimisticLock pessimisticLock = createNewPessimisticLock(document, user);
217            document.addPessimisticLock(pessimisticLock);
218        }
219
220        // find if any pessimistic locks are owned by the given user
221        for (PessimisticLock pessimisticLock : document.getPessimisticLocks()) {
222            if (pessimisticLock.isOwnedByUser(user)) {
223                return true;
224            }
225        }
226
227        return false;
228    }
229
230    /**
231     * Determines whether to add a pessimistic lock on the {@code document} for {@code user} based on the current
232     * pessimistic locks and edit modes.
233     *
234     * @param document the document on which the locks will be established
235     * @param user the user for which the locks are being established
236     * @param canEdit whether the user currently can edit the document
237     *
238     * @return true if a pessimistic lock should be added, false otherwise
239     */
240    protected boolean isPessimisticLockNeeded(Document document, Person user, boolean canEdit) {
241        List<String> userOwnedLockDescriptors = new ArrayList<String>();
242        Map<String, Set<String>> otherOwnedLockDescriptors = new HashMap<String,Set<String>>();
243
244        // create the two lock containers that help determine whether to add a pessimistic lock
245        for (PessimisticLock pessimisticLock : document.getPessimisticLocks()) {
246            if (pessimisticLock.isOwnedByUser(user)) {
247                userOwnedLockDescriptors.add(pessimisticLock.getLockDescriptor());
248            } else {
249                if (!otherOwnedLockDescriptors.containsKey(pessimisticLock.getLockDescriptor())) {
250                    otherOwnedLockDescriptors.put(pessimisticLock.getLockDescriptor(), new HashSet<String>());
251                }
252
253                String otherOwnerPrincipalId = pessimisticLock.getOwnedByUser().getPrincipalId();
254                otherOwnedLockDescriptors.get(pessimisticLock.getLockDescriptor()).add(otherOwnerPrincipalId);
255            }
256        }
257
258        // double check whether the existing pessimistic locks are sane
259        checkExistingPessimisticLocks(document, userOwnedLockDescriptors, otherOwnedLockDescriptors);
260
261        // if no one has a lock on this document, then the document can be locked if the user can edit it
262        if (userOwnedLockDescriptors.isEmpty() && otherOwnedLockDescriptors.isEmpty()) {
263            return canEdit;
264        }
265
266        // if custom locking is turned on, then if the current user doesn't have a custom lock on this document and no
267        // one else has a custom lock on this document, then the document can be locked if the user can edit it
268        if (document.useCustomLockDescriptors()) {
269            String customLockDescriptor = document.getCustomLockDescriptor(user);
270            boolean userOwnsCustomLockDescriptor = userOwnedLockDescriptors.contains(customLockDescriptor);
271            boolean otherOwnsCustomLockDescriptor = otherOwnedLockDescriptors.containsKey(customLockDescriptor);
272
273            if (!userOwnsCustomLockDescriptor && !otherOwnsCustomLockDescriptor) {
274                return canEdit;
275            }
276        }
277
278        return false;
279    }
280
281    /**
282     * Check to make sure that the current user doesn't erroneously share the same lock with another user.
283     *
284     * @param document the document to check for erroneous locks
285     * @param userOwnedLockDescriptors the list of lock descriptors for the current user
286     * @param otherOwnedLockDescriptors the map of other lock descriptors to all of the other users that own them
287     */
288    protected void checkExistingPessimisticLocks(Document document, List<String> userOwnedLockDescriptors, Map<String, Set<String>> otherOwnedLockDescriptors) {
289        for (String userOwnedLockDescriptor : userOwnedLockDescriptors) {
290            if (otherOwnedLockDescriptors.containsKey(userOwnedLockDescriptor)) {
291                Set<String> otherOwnerPrincipalIds = otherOwnedLockDescriptors.get(userOwnedLockDescriptor);
292                String workflowOwnerPrincipalId = getWorkflowPessimisticLockOwnerUser().getPrincipalId();
293                boolean hasOtherOwners = !otherOwnerPrincipalIds.isEmpty();
294                boolean hasMoreThanOneOtherOwner = otherOwnerPrincipalIds.size() > 1;
295                boolean hasWorkflowOwner = otherOwnerPrincipalIds.contains(workflowOwnerPrincipalId);
296
297                // if the lock has other owners, then if there is more than one other owner or if the single other owner
298                // is not the workflow user, then throw an error
299                if (hasOtherOwners && (hasMoreThanOneOtherOwner || !hasWorkflowOwner)) {
300                    StringBuilder builder = new StringBuilder("Found an invalid lock status on document number ");
301                    builder.append(document.getDocumentNumber());
302                    builder.append(" with current user and other user both having locks concurrently");
303
304                    if (document.useCustomLockDescriptors()) {
305                        builder.append(" for custom lock descriptor '");
306                        builder.append(userOwnedLockDescriptor);
307                        builder.append("'");
308                    }
309
310                    builder.append(".");
311
312                    LOG.debug(builder.toString());
313
314                    throw new PessimisticLockingException(builder.toString());
315                }
316            }
317        }
318    }
319
320    /**
321     * Creates a new {@link PessimisticLock} on a {@code document} for a {@code user}.
322     *
323     * <p>
324     * If the {@code document} uses custom lock descriptors, then the new lock is generated with a custom lock
325     * descriptor for a portion of the document.  Otherwise, it will create a lock over the entire document.
326     * </p>
327     *
328     * @param document the document on which the locks will be established
329     * @param user the user for which the locks are being established
330     *
331     * @return the newly created lock object
332     */
333    protected PessimisticLock createNewPessimisticLock(Document document, Person user) {
334        if (document.useCustomLockDescriptors()) {
335            return generateNewLock(document.getDocumentNumber(), document.getCustomLockDescriptor(user), user);
336        } else {
337            return generateNewLock(document.getDocumentNumber(), user);
338        }
339    }
340
341    /**
342     * {@inheritDoc}
343     */
344    @Override
345    @Deprecated
346        public Set getDocumentActions(Document document, Person user, Set<String> documentActions){
347        if(documentActions.contains(KRADConstants.KUALI_ACTION_CAN_CANCEL) && !hasPreRouteEditAuthorization(document, user) ){
348                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_CANCEL);
349        }
350        if(documentActions.contains(KRADConstants.KUALI_ACTION_CAN_SAVE)  && !hasPreRouteEditAuthorization(document, user)){
351                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_SAVE);
352        }
353        if(documentActions.contains(KRADConstants.KUALI_ACTION_CAN_ROUTE) && !hasPreRouteEditAuthorization(document, user)){
354                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_ROUTE);
355        }
356        if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE) && !hasPreRouteEditAuthorization(document, user)){
357                documentActions.remove(KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE);
358        }
359        return documentActions;
360    }
361
362
363    /**
364     * This method checks to see that the given user has a lock on the document and return true if one is found.
365     *
366     * @param document - document to check
367     * @param user - current user
368     * @return true if the document is using Pessimistic Locking, the user has initiate authorization (see
369     *         {@link #hasInitiateAuthorization(Document, Person)}), and the document has a lock owned by the given
370     *         user. If the document is not using Pessimistic Locking the value returned will be that returned by
371     *         {@link #hasInitiateAuthorization(Document, Person)}.
372     *
373     * @deprecated Different mechanism of obtaining authorization in KRAD
374     */
375    @Deprecated
376    protected boolean hasPreRouteEditAuthorization(Document document, Person user) {
377        if (document.getPessimisticLocks().isEmpty()) {
378                return true;
379        }
380        for (Iterator<PessimisticLock> iterator = document.getPessimisticLocks().iterator(); iterator.hasNext();) {
381                PessimisticLock lock = iterator.next();
382                if (lock.isOwnedByUser(user)) {
383                        return true;
384            }
385        }
386        return false;
387    }
388
389    @Deprecated
390    protected boolean usesPessimisticLocking(Document document) {
391        return getDataDictionaryService().getDataDictionary().getDocumentEntry(document.getClass().getName()).getUsePessimisticLocking();
392    }
393
394
395    /**
396     * This method creates a new {@link PessimisticLock} when Workflow processing requires one
397     *
398     * @param document - the document to create the lock against and add the lock to
399     * @see org.kuali.rice.kns.document.authorization.DocumentAuthorizer#establishWorkflowPessimisticLocking(org.kuali.rice.krad.document.Document)
400     */
401    @Override
402        public void establishWorkflowPessimisticLocking(Document document) {
403        PessimisticLock lock = createNewPessimisticLock(document, getWorkflowPessimisticLockOwnerUser());
404        document.addPessimisticLock(lock);
405    }
406
407    /**
408     * This method releases locks created via the {@link #establishWorkflowPessimisticLocking(Document)} method for the given document
409     *
410     * @param document - document to release locks from
411     * @see org.kuali.rice.kns.document.authorization.DocumentAuthorizer#releaseWorkflowPessimisticLocking(org.kuali.rice.krad.document.Document)
412     */
413    @Override
414        public void releaseWorkflowPessimisticLocking(Document document) {
415        releaseAllLocksForUser(document.getPessimisticLocks(), getWorkflowPessimisticLockOwnerUser());
416        document.refreshPessimisticLocks();
417    }
418
419    /**
420     * This method identifies the user that should be used to create and clear {@link PessimisticLock} objects required by
421     * Workflow.<br>
422     * <br>
423     * The default is the Kuali system user defined by {@link RiceConstants#SYSTEM_USER}. This method can be overriden by
424     * implementing documents if another user is needed.
425     *
426     * @return a valid {@link Person} object
427     */
428    protected Person getWorkflowPessimisticLockOwnerUser() {
429        String networkId = KRADConstants.SYSTEM_USER;
430        return getPersonService().getPersonByPrincipalName(networkId);
431    }
432
433    /**
434     * {@inheritDoc}
435     *
436     * This implementation will check the given document, editMode map, and user object to verify Pessimistic Locking. If the
437     * given edit mode map contains an 'entry type' edit mode then the system will check the locks already in existence on
438     * the document. If a valid lock for the given user is found the system will return the given edit mode map. If a valid
439     * lock is found but is owned by another user the edit mode map returned will have any 'entry type' edit modes removed. If the
440     * given document has no locks and the edit mode map passed in has at least one 'entry type' mode then a new
441     * {@link PessimisticLock} object will be created and set on the document for the given user.<br>
442     * <br>
443     * NOTE: This method is only called if the document uses pessimistic locking as described in the data dictionary file.
444     *
445     * @see org.kuali.rice.kns.document.authorization.DocumentAuthorizer#establishLocks(org.kuali.rice.krad.document.Document,
446     *      java.util.Map, org.kuali.rice.kim.api.identity.Person)
447     */
448    @Override
449    @Deprecated
450        public Map establishLocks(Document document, Map editMode, Person user) {
451        Map editModeMap = new HashMap();
452        // givenUserLockDescriptors is a list of lock descriptors currently held on the document by the given user
453        List<String> givenUserLockDescriptors = new ArrayList<String>();
454        // lockDescriptorUsers is a map with lock descriptors as keys and users other than the given user who hold a lock of each descriptor
455        Map<String,Set<Person>> lockDescriptorUsers = new HashMap<String,Set<Person>>();
456
457        // build the givenUserLockDescriptors set and the lockDescriptorUsers map
458        for (PessimisticLock lock : document.getPessimisticLocks()) {
459            if (lock.isOwnedByUser(user)) {
460                // lock is owned by given user
461                givenUserLockDescriptors.add(lock.getLockDescriptor());
462            } else {
463                // lock is not owned by the given user
464                if (!lockDescriptorUsers.containsKey(lock.getLockDescriptor())) {
465                    lockDescriptorUsers.put(lock.getLockDescriptor(), new HashSet<Person>());
466                }
467                lockDescriptorUsers.get(lock.getLockDescriptor()).add(lock.getOwnedByUser());
468            }
469        }
470
471        // verify that no locks held by current user exist for any other user
472        for (String givenUserLockDescriptor : givenUserLockDescriptors) {
473            if ( (lockDescriptorUsers.containsKey(givenUserLockDescriptor)) && (lockDescriptorUsers.get(givenUserLockDescriptor).size() > 0) ) {
474                Set<Person> users = lockDescriptorUsers.get(givenUserLockDescriptor);
475                if ( (users.size() != 1) || (!getWorkflowPessimisticLockOwnerUser().getPrincipalId().equals(users.iterator().next().getPrincipalId())) ) {
476                    String descriptorText = (document.useCustomLockDescriptors()) ? " using lock descriptor '" + givenUserLockDescriptor + "'" : "";
477                    String errorMsg = "Found an invalid lock status on document number " + document.getDocumentNumber() + "with current user and other user both having locks" + descriptorText + " concurrently";
478                    LOG.debug(errorMsg);
479                    throw new PessimisticLockingException(errorMsg);
480                }
481            }
482        }
483
484        // check to see if the given user has any locks in the system at all
485        if (givenUserLockDescriptors.isEmpty()) {
486            // given user has no locks... check for other user locks
487            if (lockDescriptorUsers.isEmpty()) {
488                // no other user has any locks... set up locks for given user if user has edit privileges
489                if (isLockRequiredByUser(document, editMode, user)) {
490                    document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
491                }
492                editModeMap.putAll(editMode);
493            } else {
494                // at least one other user has at least one other lock... adjust edit mode for read only
495                if (document.useCustomLockDescriptors()) {
496                    // check to see if the custom lock descriptor is already in use
497                    String customLockDescriptor = document.getCustomLockDescriptor(user);
498                    if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
499                        // at least one other user has this descriptor locked... remove editable edit modes
500                        editModeMap = getEditModeWithEditableModesRemoved(editMode);
501                    } else {
502                        // no other user has a lock with this descriptor
503                        if (isLockRequiredByUser(document, editMode, user)) {
504                            document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
505                        }
506                        editModeMap.putAll(editMode);
507                    }
508                } else {
509                    editModeMap = getEditModeWithEditableModesRemoved(editMode);
510                }
511            }
512        } else {
513            // given user already has at least one lock descriptor
514            if (document.useCustomLockDescriptors()) {
515                // get the custom lock descriptor and check to see if if the given user has a lock with that descriptor
516                String customLockDescriptor = document.getCustomLockDescriptor(user);
517                if (givenUserLockDescriptors.contains(customLockDescriptor)) {
518                    // user already has lock that is required
519                    editModeMap.putAll(editMode);
520                } else {
521                    // user does not have lock for descriptor required
522                    if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
523                        // another user has the lock descriptor that the given user requires... disallow lock and alter edit modes to have read only
524                        editModeMap = getEditModeWithEditableModesRemoved(editMode);
525                    } else {
526                        // no other user has a lock with this descriptor... check if this user needs a lock
527                        if (isLockRequiredByUser(document, editMode, user)) {
528                            document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
529                        }
530                        editModeMap.putAll(editMode);
531                    }
532                }
533            } else {
534                // user already has lock and no descriptors are being used... use the existing edit modes
535                editModeMap.putAll(editMode);
536            }
537        }
538
539        return editModeMap;
540    }
541
542    /**
543     * This method is used to check if the given parameters warrant a new lock to be created for the given user. This method
544     * utilizes the {@link #isEntryEditMode(java.util.Map.Entry)} method.
545     *
546     * @param document -
547     *            document to verify lock creation against
548     * @param editMode -
549     *            edit modes list to check for 'entry type' edit modes
550     * @param user -
551     *            user the lock will be 'owned' by
552     * @return true if the given edit mode map has at least one 'entry type' edit mode... false otherwise
553     */
554    @Deprecated
555    protected boolean isLockRequiredByUser(Document document, Map editMode, Person user) {
556        // check for entry edit mode
557        for (Iterator iterator = editMode.entrySet().iterator(); iterator.hasNext();) {
558            Map.Entry entry = (Map.Entry) iterator.next();
559            if (isEntryEditMode(entry)) {
560                return true;
561            }
562        }
563        return false;
564    }
565
566   /**
567     * This method is used to remove edit modes from the given map that allow the user to edit data on the document. This
568     * method utilizes the {@link #isEntryEditMode(java.util.Map.Entry)} method to identify if an edit mode is defined as an
569     * 'entry type' edit mode. It also uses the {@link #getEntryEditModeReplacementMode(java.util.Map.Entry)} method to replace
570     * any 'entry type' edit modes it finds.
571     *
572     * @param currentEditMode -
573     *            current set of edit modes the user has assigned to them
574     * @return an adjusted edit mode map where 'entry type' edit modes have been removed or replaced using the
575     *         {@link #getEntryEditModeReplacementMode} method
576     */
577    @Deprecated
578    protected Map getEditModeWithEditableModesRemoved(Map currentEditMode) {
579        Map editModeMap = new HashMap();
580        for (Iterator iterator = currentEditMode.entrySet().iterator(); iterator.hasNext();) {
581            Map.Entry entry = (Map.Entry) iterator.next();
582            if (isEntryEditMode(entry)) {
583                editModeMap.putAll(getEntryEditModeReplacementMode(entry));
584            } else {
585                editModeMap.put(entry.getKey(), entry.getValue());
586            }
587        }
588        return editModeMap;
589    }
590
591    /**
592     * This method is used to check if the given {@link Map.Entry} is an 'entry type' edit mode and that the value is set to
593     * signify that this user has that edit mode available to them
594     *
595     * @param entry -
596     *            the {@link Map.Entry} object that contains an edit mode such as the ones returned but
597     *            {@link #getEditMode(Document, Person)}
598     * @return true if the given entry has a key signifying an 'entry type' edit mode and the value is equal to
599     *         {@link #EDIT_MODE_DEFAULT_TRUE_VALUE}... false if not
600     */
601    @Deprecated
602    protected boolean isEntryEditMode(Map.Entry entry) {
603        // check for FULL_ENTRY edit mode set to default true value
604        if (AuthorizationConstants.EditMode.FULL_ENTRY.equals(entry.getKey())) {
605                String fullEntryEditModeValue = (String)entry.getValue();
606                return ( StringUtils.equalsIgnoreCase(KRADConstants.KUALI_DEFAULT_TRUE_VALUE, fullEntryEditModeValue) );
607        }
608        return false;
609    }
610
611    /**
612     * This method is used to return values needed to replace the given 'entry type' edit mode {@link Map.Entry} with one that will not allow the user to enter data on the document
613     *
614     * @param entry - the current 'entry type' edit mode to replace
615     * @return a Map of edit modes that will be used to replace this edit mode (represented by the given entry parameter)
616     */
617    protected Map getEntryEditModeReplacementMode(Map.Entry entry) {
618        Map editMode = new HashMap();
619        editMode.put(AuthorizationConstants.EditMode.VIEW_ONLY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
620        return editMode;
621    }
622
623    /**
624     * This method creates a new {@link PessimisticLock} object using the given document and user. If the document's
625     * useCustomLockDescriptors() method returns true then the new lock will also have a custom lock descriptor
626     * value set to the return value of the document's getCustomLockDescriptor(Person) method.
627     *
628     * @param document -
629     *            document to place the lock on
630     * @param editMode -
631     *            current edit modes for given user
632     * @param user -
633     *            user who will 'own' the new lock object
634     * @return the newly created lock object
635     */
636    @Deprecated
637    protected PessimisticLock createNewPessimisticLock(Document document, Map editMode, Person user) {
638        if (document.useCustomLockDescriptors()) {
639            return generateNewLock(document.getDocumentNumber(), document.getCustomLockDescriptor(user), user);
640        } else {
641            return generateNewLock(document.getDocumentNumber(), user);
642        }
643    }
644
645    public PersonService getPersonService() {
646        if ( personService == null ) {
647            personService = KimApiServiceLocator.getPersonService();
648        }
649        return personService;
650    }
651
652        public DataDictionaryService getDataDictionaryService() {
653                return dataDictionaryService;
654        }
655
656        @Required
657        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
658                this.dataDictionaryService = dataDictionaryService;
659        }
660
661        public PermissionService getPermissionService() {
662        if ( permissionService == null ) {
663                permissionService = KimApiServiceLocator.getPermissionService();
664        }
665                return permissionService;
666        }
667
668
669        @Required
670    public void setDataObjectService(DataObjectService dataObjectService) {
671                this.dataObjectService = dataObjectService;
672        }
673}
674