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