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