1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.service.impl;
17
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.commons.lang.StringUtils;
28 import org.kuali.rice.core.api.criteria.QueryByCriteria;
29 import org.kuali.rice.core.api.util.RiceConstants;
30 import org.kuali.rice.kim.api.KimConstants.PermissionNames;
31 import org.kuali.rice.kim.api.identity.Person;
32 import org.kuali.rice.kim.api.identity.PersonService;
33 import org.kuali.rice.kim.api.permission.PermissionService;
34 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
35 import org.kuali.rice.kns.authorization.AuthorizationConstants;
36 import org.kuali.rice.krad.data.DataObjectService;
37 import org.kuali.rice.krad.document.Document;
38 import org.kuali.rice.krad.document.authorization.PessimisticLock;
39 import org.kuali.rice.krad.exception.AuthorizationException;
40 import org.kuali.rice.krad.exception.PessimisticLockingException;
41 import org.kuali.rice.krad.service.DataDictionaryService;
42 import org.kuali.rice.krad.service.LegacyDataAdapter;
43 import org.kuali.rice.krad.service.PessimisticLockService;
44 import org.kuali.rice.krad.util.GlobalVariables;
45 import org.kuali.rice.krad.util.KRADConstants;
46 import org.kuali.rice.krad.util.KRADPropertyConstants;
47 import org.springframework.beans.factory.annotation.Required;
48 import org.springframework.transaction.annotation.Transactional;
49
50
51
52
53
54
55 @Transactional
56 public class PessimisticLockServiceImpl implements PessimisticLockService {
57 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PessimisticLockServiceImpl.class);
58
59 protected DataObjectService dataObjectService;
60 protected DataDictionaryService dataDictionaryService;
61 protected PermissionService permissionService;
62 protected PersonService personService;
63
64
65
66
67 @Override
68 public void delete(String id) {
69 if (StringUtils.isBlank(id)) {
70 throw new IllegalArgumentException("An invalid blank id was passed to delete a Pessimistic Lock.");
71 }
72 PessimisticLock lock = dataObjectService.find(PessimisticLock.class, Long.valueOf(id));
73 if ( lock == null ) {
74 throw new IllegalArgumentException("Pessimistic Lock with id " + id + " cannot be found in the database.");
75 }
76 Person user = GlobalVariables.getUserSession().getPerson();
77 if ( (!lock.isOwnedByUser(user)) && (!isPessimisticLockAdminUser(user)) ) {
78 throw new AuthorizationException(user.getName(),"delete", "Pessimistick Lock (id " + id + ")");
79 }
80 delete(lock);
81 }
82
83 private void delete(PessimisticLock lock) {
84 if ( LOG.isDebugEnabled() ) {
85 LOG.debug("Deleting lock: " + lock);
86 }
87 dataObjectService.delete(lock);
88 }
89
90
91
92
93 @Override
94 public PessimisticLock generateNewLock(String documentNumber) {
95 return generateNewLock(documentNumber, GlobalVariables.getUserSession().getPerson());
96 }
97
98
99
100
101 @Override
102 public PessimisticLock generateNewLock(String documentNumber, String lockDescriptor) {
103 return generateNewLock(documentNumber, lockDescriptor, GlobalVariables.getUserSession().getPerson());
104 }
105
106
107
108
109 @Override
110 public PessimisticLock generateNewLock(String documentNumber, Person user) {
111 return generateNewLock(documentNumber, PessimisticLock.DEFAULT_LOCK_DESCRIPTOR, user);
112 }
113
114
115
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
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
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
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
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
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
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
211
212 @Override
213 public boolean establishPessimisticLocks(Document document, Person user, boolean canEdit) {
214
215 if (isPessimisticLockNeeded(document, user, canEdit)) {
216 PessimisticLock pessimisticLock = createNewPessimisticLock(document, user);
217 document.addPessimisticLock(pessimisticLock);
218 }
219
220
221 for (PessimisticLock pessimisticLock : document.getPessimisticLocks()) {
222 if (pessimisticLock.isOwnedByUser(user)) {
223 return true;
224 }
225 }
226
227 return false;
228 }
229
230
231
232
233
234
235
236
237
238
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
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
259 checkExistingPessimisticLocks(document, userOwnedLockDescriptors, otherOwnedLockDescriptors);
260
261
262 if (userOwnedLockDescriptors.isEmpty() && otherOwnedLockDescriptors.isEmpty()) {
263 return canEdit;
264 }
265
266
267
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
283
284
285
286
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
298
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
322
323
324
325
326
327
328
329
330
331
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
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
365
366
367
368
369
370
371
372
373
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
397
398
399
400
401 @Override
402 public void establishWorkflowPessimisticLocking(Document document) {
403 PessimisticLock lock = createNewPessimisticLock(document, getWorkflowPessimisticLockOwnerUser());
404 document.addPessimisticLock(lock);
405 }
406
407
408
409
410
411
412
413 @Override
414 public void releaseWorkflowPessimisticLocking(Document document) {
415 releaseAllLocksForUser(document.getPessimisticLocks(), getWorkflowPessimisticLockOwnerUser());
416 document.refreshPessimisticLocks();
417 }
418
419
420
421
422
423
424
425
426
427
428 protected Person getWorkflowPessimisticLockOwnerUser() {
429 String networkId = KRADConstants.SYSTEM_USER;
430 return getPersonService().getPersonByPrincipalName(networkId);
431 }
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448 @Override
449 @Deprecated
450 public Map establishLocks(Document document, Map editMode, Person user) {
451 Map editModeMap = new HashMap();
452
453 List<String> givenUserLockDescriptors = new ArrayList<String>();
454
455 Map<String,Set<Person>> lockDescriptorUsers = new HashMap<String,Set<Person>>();
456
457
458 for (PessimisticLock lock : document.getPessimisticLocks()) {
459 if (lock.isOwnedByUser(user)) {
460
461 givenUserLockDescriptors.add(lock.getLockDescriptor());
462 } else {
463
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
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
485 if (givenUserLockDescriptors.isEmpty()) {
486
487 if (lockDescriptorUsers.isEmpty()) {
488
489 if (isLockRequiredByUser(document, editMode, user)) {
490 document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
491 }
492 editModeMap.putAll(editMode);
493 } else {
494
495 if (document.useCustomLockDescriptors()) {
496
497 String customLockDescriptor = document.getCustomLockDescriptor(user);
498 if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
499
500 editModeMap = getEditModeWithEditableModesRemoved(editMode);
501 } else {
502
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
514 if (document.useCustomLockDescriptors()) {
515
516 String customLockDescriptor = document.getCustomLockDescriptor(user);
517 if (givenUserLockDescriptors.contains(customLockDescriptor)) {
518
519 editModeMap.putAll(editMode);
520 } else {
521
522 if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
523
524 editModeMap = getEditModeWithEditableModesRemoved(editMode);
525 } else {
526
527 if (isLockRequiredByUser(document, editMode, user)) {
528 document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
529 }
530 editModeMap.putAll(editMode);
531 }
532 }
533 } else {
534
535 editModeMap.putAll(editMode);
536 }
537 }
538
539 return editModeMap;
540 }
541
542
543
544
545
546
547
548
549
550
551
552
553
554 @Deprecated
555 protected boolean isLockRequiredByUser(Document document, Map editMode, Person user) {
556
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
568
569
570
571
572
573
574
575
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
593
594
595
596
597
598
599
600
601 @Deprecated
602 protected boolean isEntryEditMode(Map.Entry entry) {
603
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
613
614
615
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
625
626
627
628
629
630
631
632
633
634
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