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.io.Serializable;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.apache.commons.lang.StringUtils;
26 import org.apache.log4j.Logger;
27 import org.kuali.rice.core.api.criteria.Predicate;
28 import org.kuali.rice.core.api.criteria.PredicateFactory;
29 import org.kuali.rice.core.api.criteria.QueryByCriteria;
30 import org.kuali.rice.core.api.util.RiceKeyConstants;
31 import org.kuali.rice.core.api.util.io.SerializationUtils;
32 import org.kuali.rice.core.framework.persistence.jta.TransactionalNoValidationExceptionRollback;
33 import org.kuali.rice.kew.api.exception.WorkflowException;
34 import org.kuali.rice.krad.bo.DataObjectBase;
35 import org.kuali.rice.krad.bo.PersistableBusinessObject;
36 import org.kuali.rice.krad.data.CopyOption;
37 import org.kuali.rice.krad.data.DataObjectService;
38 import org.kuali.rice.krad.data.KradDataServiceLocator;
39 import org.kuali.rice.krad.exception.DocumentTypeAuthorizationException;
40 import org.kuali.rice.krad.maintenance.Maintainable;
41 import org.kuali.rice.krad.maintenance.MaintenanceDocument;
42 import org.kuali.rice.krad.maintenance.MaintenanceLock;
43 import org.kuali.rice.krad.service.DataObjectAuthorizationService;
44 import org.kuali.rice.krad.service.DocumentDictionaryService;
45 import org.kuali.rice.krad.service.DocumentService;
46 import org.kuali.rice.krad.service.LegacyDataAdapter;
47 import org.kuali.rice.krad.service.MaintenanceDocumentService;
48 import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
49 import org.kuali.rice.krad.util.GlobalVariables;
50 import org.kuali.rice.krad.util.KRADConstants;
51 import org.kuali.rice.krad.util.KRADPropertyConstants;
52 import org.kuali.rice.krad.util.KRADUtils;
53 import org.springframework.beans.factory.annotation.Required;
54
55
56
57
58
59
60
61 @TransactionalNoValidationExceptionRollback
62 public class MaintenanceDocumentServiceImpl implements MaintenanceDocumentService {
63 private static final Logger LOG = Logger.getLogger(MaintenanceDocumentServiceImpl.class);
64
65 protected LegacyDataAdapter legacyDataAdapter;
66 protected DataObjectService dataObjectService;
67 protected DataObjectAuthorizationService dataObjectAuthorizationService;
68 protected DocumentService documentService;
69 protected DocumentDictionaryService documentDictionaryService;
70
71
72
73
74
75 @Override
76 @SuppressWarnings("unchecked")
77 public MaintenanceDocument setupNewMaintenanceDocument(String objectClassName, String documentTypeName,
78 String maintenanceAction) {
79 if (StringUtils.isEmpty(objectClassName) && StringUtils.isEmpty(documentTypeName)) {
80 throw new IllegalArgumentException("Document type name or bo class not given!");
81 }
82
83
84 if (StringUtils.isEmpty(documentTypeName)) {
85 try {
86 documentTypeName =
87 getDocumentDictionaryService().getMaintenanceDocumentTypeName(Class.forName(objectClassName));
88 } catch (ClassNotFoundException e) {
89 throw new RuntimeException(e);
90 }
91
92 if (StringUtils.isEmpty(documentTypeName)) {
93 throw new RuntimeException(
94 "documentTypeName is empty; does this Business Object have a maintenance document definition? " +
95 objectClassName);
96 }
97 }
98
99
100 if (KRADConstants.MAINTENANCE_NEW_ACTION.equals(maintenanceAction) ||
101 KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction)) {
102 Class<?> boClass =
103 getDocumentDictionaryService().getMaintenanceDataObjectClass(documentTypeName);
104 boolean allowsNewOrCopy = getDataObjectAuthorizationService()
105 .canCreate(boClass, GlobalVariables.getUserSession().getPerson(), documentTypeName);
106 if (!allowsNewOrCopy) {
107 LOG.error("Document type " + documentTypeName + " does not allow new or copy actions.");
108 throw new DocumentTypeAuthorizationException(
109 GlobalVariables.getUserSession().getPerson().getPrincipalId(), "newOrCopy", documentTypeName);
110 }
111 }
112
113
114 try {
115 return (MaintenanceDocument) getDocumentService().getNewDocument(documentTypeName);
116 } catch (WorkflowException e) {
117 LOG.error("Cannot get new maintenance document instance for doc type: " + documentTypeName, e);
118 throw new RuntimeException("Cannot get new maintenance document instance for doc type: " + documentTypeName,
119 e);
120 }
121 }
122
123
124
125
126 @Override
127 public void setupMaintenanceObject(MaintenanceDocument document, String maintenanceAction,
128 Map<String, String[]> requestParameters) {
129 document.getNewMaintainableObject().setMaintenanceAction(maintenanceAction);
130 document.getOldMaintainableObject().setMaintenanceAction(maintenanceAction);
131
132
133 if (KRADConstants.MAINTENANCE_DELETE_ACTION.equals(maintenanceAction))
134 {
135 checkMaintenanceActionAuthorization(document, document.getOldMaintainableObject(),
136 maintenanceAction, requestParameters);
137 }
138
139
140 if (!KRADConstants.MAINTENANCE_NEW_ACTION.equals(maintenanceAction) &&
141 !KRADConstants.MAINTENANCE_NEWWITHEXISTING_ACTION.equals(maintenanceAction)) {
142 Object oldDataObject = retrieveObjectForMaintenance(document, requestParameters);
143
144 Object newDataObject = null;
145
146
147
148
149
150 if (dataObjectService.supports(oldDataObject.getClass())) {
151 if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction)) {
152 newDataObject = dataObjectService.copyInstance(oldDataObject, CopyOption.RESET_VERSION_NUMBER,
153 CopyOption.RESET_OBJECT_ID);
154 } else {
155 newDataObject = dataObjectService.copyInstance(oldDataObject);
156 }
157 } else {
158 newDataObject = SerializationUtils.deepCopy((Serializable) oldDataObject);
159 }
160
161
162 document.getOldMaintainableObject().setDataObject(oldDataObject);
163 document.getNewMaintainableObject().setDataObject(newDataObject);
164
165 if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(maintenanceAction) && !document.isFieldsClearedOnCopy()) {
166 Maintainable maintainable = document.getNewMaintainableObject();
167
168
169
170
171 if(!dataObjectService.supports(oldDataObject.getClass())) {
172
173 if(ObjectPropertyUtils.getWriteMethod(maintainable.getDataObject().getClass(), "versionNumber") != null) {
174 ObjectPropertyUtils.setPropertyValue(maintainable.getDataObject(), "versionNumber", null);
175 }
176
177 if(ObjectPropertyUtils.getWriteMethod(maintainable.getDataObject().getClass(), "objectId") != null) {
178 ObjectPropertyUtils.setPropertyValue(maintainable.getDataObject(), "objectId", null);
179 }
180 }
181
182 clearValuesForPropertyNames(newDataObject, maintainable.getDataObjectClass());
183
184 if (!getDocumentDictionaryService().getPreserveLockingKeysOnCopy(maintainable.getDataObjectClass())) {
185 clearPrimaryKeyFields(newDataObject, maintainable.getDataObjectClass());
186 }
187 }
188
189 checkMaintenanceActionAuthorization(document, oldDataObject, maintenanceAction, requestParameters);
190 }
191
192
193 if (KRADConstants.MAINTENANCE_NEWWITHEXISTING_ACTION.equals(maintenanceAction)) {
194 Object newBO = document.getNewMaintainableObject().getDataObject();
195 Map<String, String> parameters =
196 buildKeyMapFromRequest(requestParameters, document.getNewMaintainableObject().getDataObjectClass());
197 ObjectPropertyUtils.copyPropertiesToObject(parameters, newBO);
198 if (newBO instanceof PersistableBusinessObject) {
199 ((PersistableBusinessObject) newBO).refresh();
200 }
201
202 document.getNewMaintainableObject().setupNewFromExisting(document, requestParameters);
203 } else if (KRADConstants.MAINTENANCE_NEW_ACTION.equals(maintenanceAction)) {
204 document.getNewMaintainableObject().processAfterNew(document, requestParameters);
205 }
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219 protected void checkMaintenanceActionAuthorization(MaintenanceDocument document, Object oldBusinessObject,
220 String maintenanceAction, Map<String, String[]> requestParameters) {
221 if (KRADConstants.MAINTENANCE_EDIT_ACTION.equals(maintenanceAction)) {
222 boolean allowsEdit = getDataObjectAuthorizationService()
223 .canMaintain(oldBusinessObject, GlobalVariables.getUserSession().getPerson(),
224 document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
225 if (!allowsEdit) {
226 LOG.error("Document type " + document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName() +
227 " does not allow edit actions.");
228 throw new DocumentTypeAuthorizationException(
229 GlobalVariables.getUserSession().getPerson().getPrincipalId(), "edit",
230 document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
231 }
232
233
234 document.getNewMaintainableObject().processAfterEdit(document, requestParameters);
235 } else if (KRADConstants.MAINTENANCE_DELETE_ACTION.equals(maintenanceAction)) {
236 boolean allowsDelete = getDataObjectAuthorizationService()
237 .canMaintain(oldBusinessObject, GlobalVariables.getUserSession().getPerson(),
238 document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
239
240 if (!allowsDelete) {
241 LOG.error("Document type " + document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName() +
242 " does not allow delete actions.");
243 throw new DocumentTypeAuthorizationException(
244 GlobalVariables.getUserSession().getPerson().getPrincipalId(), "delete",
245 document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
246 }
247
248 boolean dataObjectAllowsDelete = getDocumentDictionaryService().getAllowsRecordDeletion(
249 document.getOldMaintainableObject().getDataObject().getClass());
250
251 if (!dataObjectAllowsDelete) {
252 LOG.error("Document type " + document.getDocumentHeader().getWorkflowDocument().getDocumentTypeName() +
253 " does not allow delete actions.");
254 GlobalVariables.getMessageMap().removeAllWarningMessagesForProperty(KRADConstants.GLOBAL_MESSAGES);
255 GlobalVariables.getMessageMap().putError(KRADConstants.DOCUMENT_ERRORS,
256 RiceKeyConstants.MESSAGE_DELETE_ACTION_NOT_SUPPORTED);
257
258 }
259
260 }
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278 protected Object retrieveObjectForMaintenance(MaintenanceDocument document,
279 Map<String, String[]> requestParameters) {
280 Map<String, String> keyMap =
281 buildKeyMapFromRequest(requestParameters, document.getNewMaintainableObject().getDataObjectClass());
282
283 Object oldDataObject = document.getNewMaintainableObject().retrieveObjectForEditOrCopy(document, keyMap);
284
285 if (oldDataObject == null && !document.getOldMaintainableObject().isExternalBusinessObject()) {
286 throw new RuntimeException(
287 "Cannot retrieve old record for maintenance document, incorrect parameters passed on maint url: " +
288 requestParameters);
289 }
290
291 if (document.getOldMaintainableObject().isExternalBusinessObject()) {
292 if (oldDataObject == null) {
293 try {
294 oldDataObject = document.getOldMaintainableObject().getDataObjectClass().newInstance();
295 } catch (Exception ex) {
296 throw new RuntimeException(
297 "External BO maintainable was null and unable to instantiate for old maintainable object.",
298 ex);
299 }
300 }
301
302 populateMaintenanceObjectWithCopyKeyValues(KRADUtils.translateRequestParameterMap(requestParameters),
303 oldDataObject, document.getOldMaintainableObject());
304 document.getOldMaintainableObject().prepareExternalBusinessObject((PersistableBusinessObject) oldDataObject);
305 oldDataObject = document.getOldMaintainableObject().getDataObject();
306 }
307
308 return oldDataObject;
309 }
310
311
312
313
314
315
316
317 protected void clearPrimaryKeyFields(Object maintenanceObject, Class<?> dataObjectClass) {
318 List<String> keyFieldNames = legacyDataAdapter.listPrimaryKeyFieldNames(dataObjectClass);
319 for (String keyFieldName : keyFieldNames) {
320 ObjectPropertyUtils.setPropertyValue(maintenanceObject, keyFieldName, null);
321 }
322 }
323
324
325
326
327
328
329
330 protected void clearValuesForPropertyNames(Object maintenanceObject, Class<?> dataObjectClass) {
331 List<String> clearValueOnCopyPropertyNames = getDocumentDictionaryService().getClearValueOnCopyPropertyNames(
332 dataObjectClass);
333
334 for (String clearValueOnCopyPropertyName : clearValueOnCopyPropertyNames) {
335 if (!StringUtils.contains(clearValueOnCopyPropertyName, ".")) {
336 if (ObjectPropertyUtils.isWritableProperty(maintenanceObject, clearValueOnCopyPropertyName)) {
337 ObjectPropertyUtils.setPropertyValue(maintenanceObject, clearValueOnCopyPropertyName, null);
338 }
339 } else {
340 String objectName = StringUtils.substringBeforeLast(clearValueOnCopyPropertyName, ".");
341 String objectToClear = StringUtils.substringAfterLast(clearValueOnCopyPropertyName, ".");
342
343 clearValuesInNestedObjects(objectName, maintenanceObject, objectToClear);
344 }
345 }
346 }
347
348
349
350
351
352
353
354
355 private void clearValuesInNestedObjects(String objectName, Object maintenanceObject, String objectToClear) {
356 if (objectName.contains(".")) {
357 String newObjectName = StringUtils.substringAfter(objectName, ".");
358 objectName = StringUtils.substringBefore(objectName, ".");
359
360 if (ObjectPropertyUtils.getPropertyValue(maintenanceObject, objectName) instanceof Collection<?>) {
361 Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(maintenanceObject, objectName);
362
363 for (Object object : collection) {
364 clearValuesInNestedObjects(newObjectName, object, objectToClear);
365 }
366 } else {
367 Object object = ObjectPropertyUtils.getPropertyValue(maintenanceObject, objectName);
368 clearValuesInNestedObjects(newObjectName, object, objectToClear);
369 }
370 } else {
371 if (ObjectPropertyUtils.getPropertyValue(maintenanceObject, objectName) instanceof Collection<?>) {
372 Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(maintenanceObject, objectName);
373
374 for (Object object : collection) {
375 if (ObjectPropertyUtils.isWritableProperty(object, objectToClear)) {
376 ObjectPropertyUtils.setPropertyValue(object, objectToClear, null);
377 }
378 }
379 } else {
380 Object object = ObjectPropertyUtils.getPropertyValue(maintenanceObject, objectName);
381
382 if (ObjectPropertyUtils.isWritableProperty(object, objectToClear)) {
383 ObjectPropertyUtils.setPropertyValue(object, objectToClear, null);
384 }
385 }
386 }
387 }
388
389
390
391
392
393
394
395
396
397
398
399 protected Map<String, String> buildKeyMapFromRequest(Map<String, String[]> requestParameters,
400 Class<?> dataObjectClass) {
401 List<String> keyFieldNames = null;
402
403
404 Map<String, String> parameters = KRADUtils.translateRequestParameterMap(requestParameters);
405
406
407
408 if (!StringUtils.isBlank(parameters.get(KRADConstants.OVERRIDE_KEYS))) {
409 String[] overrideKeys =
410 parameters.get(KRADConstants.OVERRIDE_KEYS).split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
411 keyFieldNames = Arrays.asList(overrideKeys);
412 } else {
413 keyFieldNames = legacyDataAdapter.listPrimaryKeyFieldNames(dataObjectClass);
414 }
415
416 return KRADUtils.getParametersFromRequest(keyFieldNames, dataObjectClass, parameters);
417 }
418
419
420
421
422
423
424
425
426
427
428
429 protected void populateMaintenanceObjectWithCopyKeyValues(Map<String, String> parameters, Object oldBusinessObject,
430 Maintainable oldMaintainableObject) {
431 List<String> keyFieldNamesToCopy = null;
432 Map<String, String> parametersToCopy = null;
433
434 if (!StringUtils.isBlank(parameters.get(KRADConstants.COPY_KEYS))) {
435 String[] copyKeys =
436 parameters.get(KRADConstants.COPY_KEYS).split(KRADConstants.FIELD_CONVERSIONS_SEPARATOR);
437 keyFieldNamesToCopy = Arrays.asList(copyKeys);
438 parametersToCopy = KRADUtils
439 .getParametersFromRequest(keyFieldNamesToCopy, oldMaintainableObject.getDataObjectClass(),
440 parameters);
441 }
442
443 if (parametersToCopy != null) {
444
445 ObjectPropertyUtils.copyPropertiesToObject(parametersToCopy, oldBusinessObject);
446 }
447 }
448
449
450
451
452 @Override
453 public String getLockingDocumentId(MaintenanceDocument document) {
454 return getLockingDocumentId(document.getNewMaintainableObject(), document.getDocumentNumber());
455 }
456
457
458
459
460
461 @Override
462 public String getLockingDocumentId(Maintainable maintainable, final String documentNumber) {
463 final List<MaintenanceLock> maintenanceLocks = maintainable.generateMaintenanceLocks();
464 String lockingDocId = null;
465 for (MaintenanceLock maintenanceLock : maintenanceLocks) {
466 lockingDocId = getLockingDocumentNumber(maintenanceLock.getLockingRepresentation(),
467 documentNumber);
468 if (StringUtils.isNotBlank(lockingDocId)) {
469 break;
470 }
471 }
472
473 return lockingDocId;
474 }
475
476 protected String getLockingDocumentNumber(String lockingRepresentation, String documentNumber) {
477 String lockingDocNumber = "";
478
479
480 List<Predicate> predicates = new ArrayList<Predicate>();
481 predicates.add(PredicateFactory.equal("lockingRepresentation", lockingRepresentation));
482
483
484
485 if (StringUtils.isNotBlank(documentNumber)) {
486 predicates.add(PredicateFactory.notEqual(KRADPropertyConstants.DOCUMENT_NUMBER, documentNumber));
487 }
488
489 QueryByCriteria.Builder qbc = QueryByCriteria.Builder.create();
490 qbc.setPredicates(PredicateFactory.and(predicates.toArray(new Predicate[predicates.size()])));
491
492
493 List<MaintenanceLock> results = KradDataServiceLocator.getDataObjectService().findMatching(MaintenanceLock.class, qbc.build())
494 .getResults();
495 if (results.size() > 1) {
496 throw new IllegalStateException(
497 "Expected single result querying for MaintenanceLock. LockRep: " + lockingRepresentation);
498 }
499
500
501
502 if (!results.isEmpty()) {
503 lockingDocNumber = results.get(0).getDocumentNumber();
504 }
505 return lockingDocNumber;
506 }
507
508
509
510
511 @Override
512 public void deleteLocks(String documentNumber) {
513 dataObjectService.deleteMatching(MaintenanceLock.class, QueryByCriteria.Builder.forAttribute(
514 "documentNumber", documentNumber).build());
515 }
516
517
518
519
520 @Override
521 public void storeLocks(List<MaintenanceLock> maintenanceLocks) {
522 if (maintenanceLocks == null) {
523 return;
524 }
525 for (MaintenanceLock maintenanceLock : maintenanceLocks) {
526 dataObjectService.save(maintenanceLock);
527 }
528 }
529
530 protected DataObjectAuthorizationService getDataObjectAuthorizationService() {
531 return dataObjectAuthorizationService;
532 }
533
534 @Required
535 public void setDataObjectAuthorizationService(DataObjectAuthorizationService dataObjectAuthorizationService) {
536 this.dataObjectAuthorizationService = dataObjectAuthorizationService;
537 }
538
539 protected DocumentService getDocumentService() {
540 return this.documentService;
541 }
542
543 @Required
544 public void setDocumentService(DocumentService documentService) {
545 this.documentService = documentService;
546 }
547
548 public DocumentDictionaryService getDocumentDictionaryService() {
549 return documentDictionaryService;
550 }
551
552 @Required
553 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
554 this.documentDictionaryService = documentDictionaryService;
555 }
556
557 @Required
558 public void setDataObjectService(DataObjectService dataObjectService) {
559 this.dataObjectService = dataObjectService;
560 }
561
562 @Required
563 public void setLegacyDataAdapter(LegacyDataAdapter legacyDataAdapter) {
564 this.legacyDataAdapter = legacyDataAdapter;
565 }
566
567 }