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.apache.ojb.broker.OptimisticLockException;
29 import org.kuali.rice.core.api.criteria.QueryByCriteria;
30 import org.kuali.rice.core.api.util.RiceConstants;
31 import org.kuali.rice.kim.api.KimConstants.PermissionNames;
32 import org.kuali.rice.kim.api.identity.Person;
33 import org.kuali.rice.kim.api.identity.PersonService;
34 import org.kuali.rice.kim.api.permission.PermissionService;
35 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
36 import org.kuali.rice.kns.authorization.AuthorizationConstants;
37 import org.kuali.rice.krad.data.DataObjectService;
38 import org.kuali.rice.krad.document.Document;
39 import org.kuali.rice.krad.document.authorization.PessimisticLock;
40 import org.kuali.rice.krad.exception.AuthorizationException;
41 import org.kuali.rice.krad.exception.PessimisticLockingException;
42 import org.kuali.rice.krad.service.DataDictionaryService;
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 = (PessimisticLock) 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 = (PessimisticLock) iterator.next();
163 if (lock.isOwnedByUser(user)) {
164 try {
165 delete(lock);
166 } catch ( RuntimeException ex ) {
167 if ( ex.getCause() instanceof OptimisticLockException) {
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 = (PessimisticLock) iterator.next();
184 if ( (lock.isOwnedByUser(user)) && (lockDescriptor.equals(lock.getLockDescriptor())) ) {
185 try {
186 delete(lock);
187 } catch ( RuntimeException ex ) {
188 if ( ex.getCause() instanceof OptimisticLockException ) {
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
213
214 @Override
215 public Set getDocumentActions(Document document, Person user, Set<String> documentActions){
216 if(documentActions.contains(KRADConstants.KUALI_ACTION_CAN_CANCEL) && !hasPreRouteEditAuthorization(document, user) ){
217 documentActions.remove(KRADConstants.KUALI_ACTION_CAN_CANCEL);
218 }
219 if(documentActions.contains(KRADConstants.KUALI_ACTION_CAN_SAVE) && !hasPreRouteEditAuthorization(document, user)){
220 documentActions.remove(KRADConstants.KUALI_ACTION_CAN_SAVE);
221 }
222 if(documentActions.contains(KRADConstants.KUALI_ACTION_CAN_ROUTE) && !hasPreRouteEditAuthorization(document, user)){
223 documentActions.remove(KRADConstants.KUALI_ACTION_CAN_ROUTE);
224 }
225 if (documentActions.contains(KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE) && !hasPreRouteEditAuthorization(document, user)){
226 documentActions.remove(KRADConstants.KUALI_ACTION_CAN_BLANKET_APPROVE);
227 }
228 return documentActions;
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242 protected boolean hasPreRouteEditAuthorization(Document document, Person user) {
243 if (document.getPessimisticLocks().isEmpty()) {
244 return true;
245 }
246 for (Iterator<PessimisticLock> iterator = document.getPessimisticLocks().iterator(); iterator.hasNext();) {
247 PessimisticLock lock = iterator.next();
248 if (lock.isOwnedByUser(user)) {
249 return true;
250 }
251 }
252 return false;
253 }
254
255
256 protected boolean usesPessimisticLocking(Document document) {
257 return getDataDictionaryService().getDataDictionary().getDocumentEntry(document.getClass().getName()).getUsePessimisticLocking();
258 }
259
260
261
262
263
264
265
266
267 @Override
268 public void establishWorkflowPessimisticLocking(Document document) {
269 PessimisticLock lock = createNewPessimisticLock(document, new HashMap(), getWorkflowPessimisticLockOwnerUser());
270 document.addPessimisticLock(lock);
271 }
272
273
274
275
276
277
278
279 @Override
280 public void releaseWorkflowPessimisticLocking(Document document) {
281 releaseAllLocksForUser(document.getPessimisticLocks(), getWorkflowPessimisticLockOwnerUser());
282 document.refreshPessimisticLocks();
283 }
284
285
286
287
288
289
290
291
292
293
294 protected Person getWorkflowPessimisticLockOwnerUser() {
295 String networkId = KRADConstants.SYSTEM_USER;
296 return getPersonService().getPersonByPrincipalName(networkId);
297 }
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312 @Override
313 public Map establishLocks(Document document, Map editMode, Person user) {
314 Map editModeMap = new HashMap();
315
316 List<String> givenUserLockDescriptors = new ArrayList<String>();
317
318 Map<String,Set<Person>> lockDescriptorUsers = new HashMap<String,Set<Person>>();
319
320
321 for (PessimisticLock lock : document.getPessimisticLocks()) {
322 if (lock.isOwnedByUser(user)) {
323
324 givenUserLockDescriptors.add(lock.getLockDescriptor());
325 } else {
326
327 if (!lockDescriptorUsers.containsKey(lock.getLockDescriptor())) {
328 lockDescriptorUsers.put(lock.getLockDescriptor(), new HashSet<Person>());
329 }
330 ((Set<Person>) lockDescriptorUsers.get(lock.getLockDescriptor())).add(lock.getOwnedByUser());
331 }
332 }
333
334
335 for (String givenUserLockDescriptor : givenUserLockDescriptors) {
336 if ( (lockDescriptorUsers.containsKey(givenUserLockDescriptor)) && (lockDescriptorUsers.get(givenUserLockDescriptor).size() > 0) ) {
337 Set<Person> users = lockDescriptorUsers.get(givenUserLockDescriptor);
338 if ( (users.size() != 1) || (!getWorkflowPessimisticLockOwnerUser().getPrincipalId().equals(users.iterator().next().getPrincipalId())) ) {
339 String descriptorText = (document.useCustomLockDescriptors()) ? " using lock descriptor '" + givenUserLockDescriptor + "'" : "";
340 String errorMsg = "Found an invalid lock status on document number " + document.getDocumentNumber() + "with current user and other user both having locks" + descriptorText + " concurrently";
341 LOG.debug(errorMsg);
342 throw new PessimisticLockingException(errorMsg);
343 }
344 }
345 }
346
347
348 if (givenUserLockDescriptors.isEmpty()) {
349
350 if (lockDescriptorUsers.isEmpty()) {
351
352 if (isLockRequiredByUser(document, editMode, user)) {
353 document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
354 }
355 editModeMap.putAll(editMode);
356 } else {
357
358 if (document.useCustomLockDescriptors()) {
359
360 String customLockDescriptor = document.getCustomLockDescriptor(user);
361 if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
362
363 editModeMap = getEditModeWithEditableModesRemoved(editMode);
364 } else {
365
366 if (isLockRequiredByUser(document, editMode, user)) {
367 document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
368 }
369 editModeMap.putAll(editMode);
370 }
371 } else {
372 editModeMap = getEditModeWithEditableModesRemoved(editMode);
373 }
374 }
375 } else {
376
377 if (document.useCustomLockDescriptors()) {
378
379 String customLockDescriptor = document.getCustomLockDescriptor(user);
380 if (givenUserLockDescriptors.contains(customLockDescriptor)) {
381
382 editModeMap.putAll(editMode);
383 } else {
384
385 if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
386
387 editModeMap = getEditModeWithEditableModesRemoved(editMode);
388 } else {
389
390 if (isLockRequiredByUser(document, editMode, user)) {
391 document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
392 }
393 editModeMap.putAll(editMode);
394 }
395 }
396 } else {
397
398 editModeMap.putAll(editMode);
399 }
400 }
401
402 return editModeMap;
403 }
404
405
406
407
408
409
410
411
412
413
414
415
416
417 protected boolean isLockRequiredByUser(Document document, Map editMode, Person user) {
418
419 for (Iterator iterator = editMode.entrySet().iterator(); iterator.hasNext();) {
420 Map.Entry entry = (Map.Entry) iterator.next();
421 if (isEntryEditMode(entry)) {
422 return true;
423 }
424 }
425 return false;
426 }
427
428
429
430
431
432
433
434
435
436
437
438
439 protected Map getEditModeWithEditableModesRemoved(Map currentEditMode) {
440 Map editModeMap = new HashMap();
441 for (Iterator iterator = currentEditMode.entrySet().iterator(); iterator.hasNext();) {
442 Map.Entry entry = (Map.Entry) iterator.next();
443 if (isEntryEditMode(entry)) {
444 editModeMap.putAll(getEntryEditModeReplacementMode(entry));
445 } else {
446 editModeMap.put(entry.getKey(), entry.getValue());
447 }
448 }
449 return editModeMap;
450 }
451
452
453
454
455
456
457
458
459
460
461
462 protected boolean isEntryEditMode(Map.Entry entry) {
463
464 if (AuthorizationConstants.EditMode.FULL_ENTRY.equals(entry.getKey())) {
465 String fullEntryEditModeValue = (String)entry.getValue();
466 return ( StringUtils.equalsIgnoreCase(KRADConstants.KUALI_DEFAULT_TRUE_VALUE, fullEntryEditModeValue) );
467 }
468 return false;
469 }
470
471
472
473
474
475
476
477 protected Map getEntryEditModeReplacementMode(Map.Entry entry) {
478 Map editMode = new HashMap();
479 editMode.put(AuthorizationConstants.EditMode.VIEW_ONLY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
480 return editMode;
481 }
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496 protected PessimisticLock createNewPessimisticLock(Document document, Map editMode, Person user) {
497 if (document.useCustomLockDescriptors()) {
498 return generateNewLock(document.getDocumentNumber(), document.getCustomLockDescriptor(user), user);
499 } else {
500 return generateNewLock(document.getDocumentNumber(), user);
501 }
502 }
503
504 public PersonService getPersonService() {
505 if ( personService == null ) {
506 personService = KimApiServiceLocator.getPersonService();
507 }
508 return personService;
509 }
510
511 public DataDictionaryService getDataDictionaryService() {
512 return dataDictionaryService;
513 }
514
515 @Required
516 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
517 this.dataDictionaryService = dataDictionaryService;
518 }
519
520 public PermissionService getPermissionService() {
521 if ( permissionService == null ) {
522 permissionService = KimApiServiceLocator.getPermissionService();
523 }
524 return permissionService;
525 }
526
527
528 @Required
529 public void setDataObjectService(DataObjectService dataObjectService) {
530 this.dataObjectService = dataObjectService;
531 }
532 }
533