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