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 org.apache.commons.lang.StringUtils;
19 import org.apache.ojb.broker.OptimisticLockException;
20 import org.kuali.rice.core.api.util.RiceConstants;
21 import org.kuali.rice.kim.api.KimConstants.PermissionNames;
22 import org.kuali.rice.kim.api.identity.Person;
23 import org.kuali.rice.kim.api.identity.PersonService;
24 import org.kuali.rice.kim.api.permission.PermissionService;
25 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
26 import org.kuali.rice.kns.authorization.AuthorizationConstants;
27 import org.kuali.rice.krad.UserSession;
28 import org.kuali.rice.krad.document.Document;
29 import org.kuali.rice.krad.document.authorization.PessimisticLock;
30 import org.kuali.rice.krad.exception.AuthorizationException;
31 import org.kuali.rice.krad.exception.PessimisticLockingException;
32 import org.kuali.rice.krad.service.BusinessObjectService;
33 import org.kuali.rice.krad.service.DataDictionaryService;
34 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
35 import org.kuali.rice.krad.service.PessimisticLockService;
36 import org.kuali.rice.krad.util.GlobalVariables;
37 import org.kuali.rice.krad.util.KRADConstants;
38 import org.kuali.rice.krad.util.KRADPropertyConstants;
39 import org.kuali.rice.krad.util.ObjectUtils;
40 import org.springframework.transaction.annotation.Transactional;
41
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Set;
50
51
52
53
54
55
56
57 @Transactional
58 public class PessimisticLockServiceImpl implements PessimisticLockService {
59 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PessimisticLockServiceImpl.class);
60
61 private PersonService personService;
62 private BusinessObjectService businessObjectService;
63 private DataDictionaryService dataDictionaryService;
64 private PermissionService permissionService;
65
66
67
68
69 public void delete(String id) {
70 if (StringUtils.isBlank(id)) {
71 throw new IllegalArgumentException("An invalid blank id was passed to delete a Pessimistic Lock.");
72 }
73 Map<String,Object> primaryKeys = new HashMap<String,Object>();
74 primaryKeys.put(KRADPropertyConstants.ID, Long.valueOf(id));
75 PessimisticLock lock = (PessimisticLock) getBusinessObjectService().findByPrimaryKey(PessimisticLock.class, primaryKeys);
76 if (ObjectUtils.isNull(lock)) {
77 throw new IllegalArgumentException("Pessimistic Lock with id " + id + " cannot be found in the database.");
78 }
79 Person user = GlobalVariables.getUserSession().getPerson();
80 if ( (!lock.isOwnedByUser(user)) && (!isPessimisticLockAdminUser(user)) ) {
81 throw new AuthorizationException(user.getName(),"delete", "Pessimistick Lock (id " + id + ")");
82 }
83 delete(lock);
84 }
85
86 private void delete(PessimisticLock lock) {
87 if ( LOG.isDebugEnabled() ) {
88 LOG.debug("Deleting lock: " + lock);
89 }
90 getBusinessObjectService().delete(lock);
91 }
92
93
94
95
96 public PessimisticLock generateNewLock(String documentNumber) {
97 return generateNewLock(documentNumber, GlobalVariables.getUserSession().getPerson());
98 }
99
100
101
102
103 public PessimisticLock generateNewLock(String documentNumber, String lockDescriptor) {
104 return generateNewLock(documentNumber, lockDescriptor, GlobalVariables.getUserSession().getPerson());
105 }
106
107
108
109
110 public PessimisticLock generateNewLock(String documentNumber, Person user) {
111 return generateNewLock(documentNumber, PessimisticLock.DEFAULT_LOCK_DESCRIPTOR, user);
112 }
113
114
115
116
117 public PessimisticLock generateNewLock(String documentNumber, String lockDescriptor, Person user) {
118 PessimisticLock lock = new PessimisticLock(documentNumber, lockDescriptor, user, GlobalVariables.getUserSession());
119 lock = save(lock);
120 if ( LOG.isDebugEnabled() ) {
121 LOG.debug("Generated new lock: " + lock);
122 }
123 return lock;
124 }
125
126
127
128
129 public List<PessimisticLock> getPessimisticLocksForDocument(String documentNumber) {
130 Map fieldValues = new HashMap();
131 fieldValues.put(KRADPropertyConstants.DOCUMENT_NUMBER, documentNumber);
132 return (List<PessimisticLock>) getBusinessObjectService().findMatching(PessimisticLock.class, fieldValues);
133 }
134
135
136
137
138 public List<PessimisticLock> getPessimisticLocksForSession(String sessionId) {
139 Map fieldValues = new HashMap();
140 fieldValues.put(KRADPropertyConstants.SESSION_ID, sessionId);
141 return (List<PessimisticLock>) getBusinessObjectService().findMatching(PessimisticLock.class, fieldValues);
142 }
143
144
145
146
147 public boolean isPessimisticLockAdminUser(Person user) {
148 return getPermissionService().isAuthorized( user.getPrincipalId(), KRADConstants.KNS_NAMESPACE, PermissionNames.ADMIN_PESSIMISTIC_LOCKING,
149 Collections.<String, String>emptyMap() );
150 }
151
152
153
154
155 public void releaseAllLocksForUser(List<PessimisticLock> locks, Person user) {
156 for (Iterator<PessimisticLock> iterator = locks.iterator(); iterator.hasNext();) {
157 PessimisticLock lock = (PessimisticLock) iterator.next();
158 if (lock.isOwnedByUser(user)) {
159 try {
160 delete(lock);
161 } catch ( RuntimeException ex ) {
162 if ( ex.getCause() instanceof OptimisticLockException) {
163 LOG.warn( "Suppressing Optimistic Lock Exception. Document Num: " + lock.getDocumentNumber());
164 } else {
165 throw ex;
166 }
167 }
168 }
169 }
170 }
171
172
173
174
175 public void releaseAllLocksForUser(List<PessimisticLock> locks, Person user, String lockDescriptor) {
176 for (Iterator<PessimisticLock> iterator = locks.iterator(); iterator.hasNext();) {
177 PessimisticLock lock = (PessimisticLock) iterator.next();
178 if ( (lock.isOwnedByUser(user)) && (lockDescriptor.equals(lock.getLockDescriptor())) ) {
179 try {
180 delete(lock);
181 } catch ( RuntimeException ex ) {
182 if ( ex.getCause() instanceof OptimisticLockException ) {
183 LOG.warn( "Suppressing Optimistic Lock Exception. Document Num: " + lock.getDocumentNumber());
184 } else {
185 throw ex;
186 }
187 }
188 }
189 }
190 }
191
192
193
194
195 public PessimisticLock save(PessimisticLock lock) {
196 if ( LOG.isDebugEnabled() ) {
197 LOG.debug("Saving lock: " + lock);
198 }
199 return (PessimisticLock)getBusinessObjectService().save(lock);
200 }
201
202 public BusinessObjectService getBusinessObjectService() {
203 return this.businessObjectService;
204 }
205
206 public void setBusinessObjectService(BusinessObjectService businessObjectService) {
207 this.businessObjectService = businessObjectService;
208 }
209
210
211
212
213
214
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 iterator = document.getPessimisticLocks().iterator(); iterator.hasNext();) {
247 PessimisticLock lock = (PessimisticLock) 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 public void establishWorkflowPessimisticLocking(Document document) {
268 PessimisticLock lock = createNewPessimisticLock(document, new HashMap(), getWorkflowPessimisticLockOwnerUser());
269 document.addPessimisticLock(lock);
270 }
271
272
273
274
275
276
277
278 public void releaseWorkflowPessimisticLocking(Document document) {
279 releaseAllLocksForUser(document.getPessimisticLocks(), getWorkflowPessimisticLockOwnerUser());
280 document.refreshPessimisticLocks();
281 }
282
283
284
285
286
287
288
289
290
291
292 protected Person getWorkflowPessimisticLockOwnerUser() {
293 String networkId = KRADConstants.SYSTEM_USER;
294 return getPersonService().getPersonByPrincipalName(networkId);
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310 public Map establishLocks(Document document, Map editMode, Person user) {
311 Map editModeMap = new HashMap();
312
313 List<String> givenUserLockDescriptors = new ArrayList<String>();
314
315 Map<String,Set<Person>> lockDescriptorUsers = new HashMap<String,Set<Person>>();
316
317
318 for (PessimisticLock lock : document.getPessimisticLocks()) {
319 if (lock.isOwnedByUser(user)) {
320
321 givenUserLockDescriptors.add(lock.getLockDescriptor());
322 } else {
323
324 if (!lockDescriptorUsers.containsKey(lock.getLockDescriptor())) {
325 lockDescriptorUsers.put(lock.getLockDescriptor(), new HashSet<Person>());
326 }
327 ((Set<Person>) lockDescriptorUsers.get(lock.getLockDescriptor())).add(lock.getOwnedByUser());
328 }
329 }
330
331
332 for (String givenUserLockDescriptor : givenUserLockDescriptors) {
333 if ( (lockDescriptorUsers.containsKey(givenUserLockDescriptor)) && (lockDescriptorUsers.get(givenUserLockDescriptor).size() > 0) ) {
334 Set<Person> users = lockDescriptorUsers.get(givenUserLockDescriptor);
335 if ( (users.size() != 1) || (!getWorkflowPessimisticLockOwnerUser().getPrincipalId().equals(users.iterator().next().getPrincipalId())) ) {
336 String descriptorText = (document.useCustomLockDescriptors()) ? " using lock descriptor '" + givenUserLockDescriptor + "'" : "";
337 String errorMsg = "Found an invalid lock status on document number " + document.getDocumentNumber() + "with current user and other user both having locks" + descriptorText + " concurrently";
338 LOG.debug(errorMsg);
339 throw new PessimisticLockingException(errorMsg);
340 }
341 }
342 }
343
344
345 if (givenUserLockDescriptors.isEmpty()) {
346
347 if (lockDescriptorUsers.isEmpty()) {
348
349 if (isLockRequiredByUser(document, editMode, user)) {
350 document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
351 }
352 editModeMap.putAll(editMode);
353 } else {
354
355 if (document.useCustomLockDescriptors()) {
356
357 String customLockDescriptor = document.getCustomLockDescriptor(user);
358 if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
359
360 editModeMap = getEditModeWithEditableModesRemoved(editMode);
361 } else {
362
363 if (isLockRequiredByUser(document, editMode, user)) {
364 document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
365 }
366 editModeMap.putAll(editMode);
367 }
368 } else {
369 editModeMap = getEditModeWithEditableModesRemoved(editMode);
370 }
371 }
372 } else {
373
374 if (document.useCustomLockDescriptors()) {
375
376 String customLockDescriptor = document.getCustomLockDescriptor(user);
377 if (givenUserLockDescriptors.contains(customLockDescriptor)) {
378
379 editModeMap.putAll(editMode);
380 } else {
381
382 if (lockDescriptorUsers.containsKey(customLockDescriptor)) {
383
384 editModeMap = getEditModeWithEditableModesRemoved(editMode);
385 } else {
386
387 if (isLockRequiredByUser(document, editMode, user)) {
388 document.addPessimisticLock(createNewPessimisticLock(document, editMode, user));
389 }
390 editModeMap.putAll(editMode);
391 }
392 }
393 } else {
394
395 editModeMap.putAll(editMode);
396 }
397 }
398
399 return editModeMap;
400 }
401
402
403
404
405
406
407
408
409
410
411
412
413
414 protected boolean isLockRequiredByUser(Document document, Map editMode, Person user) {
415
416 for (Iterator iterator = editMode.entrySet().iterator(); iterator.hasNext();) {
417 Map.Entry entry = (Map.Entry) iterator.next();
418 if (isEntryEditMode(entry)) {
419 return true;
420 }
421 }
422 return false;
423 }
424
425
426
427
428
429
430
431
432
433
434
435
436 protected Map getEditModeWithEditableModesRemoved(Map currentEditMode) {
437 Map editModeMap = new HashMap();
438 for (Iterator iterator = currentEditMode.entrySet().iterator(); iterator.hasNext();) {
439 Map.Entry entry = (Map.Entry) iterator.next();
440 if (isEntryEditMode(entry)) {
441 editModeMap.putAll(getEntryEditModeReplacementMode(entry));
442 } else {
443 editModeMap.put(entry.getKey(), entry.getValue());
444 }
445 }
446 return editModeMap;
447 }
448
449
450
451
452
453
454
455
456
457
458
459 protected boolean isEntryEditMode(Map.Entry entry) {
460
461 if (AuthorizationConstants.EditMode.FULL_ENTRY.equals(entry.getKey())) {
462 String fullEntryEditModeValue = (String)entry.getValue();
463 return ( StringUtils.equalsIgnoreCase(KRADConstants.KUALI_DEFAULT_TRUE_VALUE, fullEntryEditModeValue) );
464 }
465 return false;
466 }
467
468
469
470
471
472
473
474 protected Map getEntryEditModeReplacementMode(Map.Entry entry) {
475 Map editMode = new HashMap();
476 editMode.put(AuthorizationConstants.EditMode.VIEW_ONLY, KRADConstants.KUALI_DEFAULT_TRUE_VALUE);
477 return editMode;
478 }
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493 protected PessimisticLock createNewPessimisticLock(Document document, Map editMode, Person user) {
494 if (document.useCustomLockDescriptors()) {
495 return generateNewLock(document.getDocumentNumber(), document.getCustomLockDescriptor(user), user);
496 } else {
497 return generateNewLock(document.getDocumentNumber(), user);
498 }
499 }
500
501 public PersonService getPersonService() {
502 if ( personService == null ) {
503 personService = KimApiServiceLocator.getPersonService();
504 }
505 return personService;
506 }
507
508 public DataDictionaryService getDataDictionaryService() {
509 if ( dataDictionaryService == null ) {
510 dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
511 }
512 return dataDictionaryService;
513 }
514
515 public PermissionService getPermissionService() {
516 if ( permissionService == null ) {
517 permissionService = KimApiServiceLocator.getPermissionService();
518 }
519 return permissionService;
520 }
521
522
523
524 }
525