001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.service.impl;
017
018import java.io.Serializable;
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collection;
022import java.util.List;
023import java.util.Map;
024
025import org.apache.commons.lang.StringUtils;
026import org.apache.log4j.Logger;
027import org.kuali.rice.core.api.criteria.Predicate;
028import org.kuali.rice.core.api.criteria.PredicateFactory;
029import org.kuali.rice.core.api.criteria.QueryByCriteria;
030import org.kuali.rice.core.api.util.RiceKeyConstants;
031import org.kuali.rice.core.api.util.io.SerializationUtils;
032import org.kuali.rice.core.framework.persistence.jta.TransactionalNoValidationExceptionRollback;
033import org.kuali.rice.kew.api.exception.WorkflowException;
034import org.kuali.rice.krad.bo.DataObjectBase;
035import org.kuali.rice.krad.bo.PersistableBusinessObject;
036import org.kuali.rice.krad.data.CopyOption;
037import org.kuali.rice.krad.data.DataObjectService;
038import org.kuali.rice.krad.data.KradDataServiceLocator;
039import org.kuali.rice.krad.exception.DocumentTypeAuthorizationException;
040import org.kuali.rice.krad.maintenance.Maintainable;
041import org.kuali.rice.krad.maintenance.MaintenanceDocument;
042import org.kuali.rice.krad.maintenance.MaintenanceLock;
043import org.kuali.rice.krad.service.DataObjectAuthorizationService;
044import org.kuali.rice.krad.service.DocumentDictionaryService;
045import org.kuali.rice.krad.service.DocumentService;
046import org.kuali.rice.krad.service.LegacyDataAdapter;
047import org.kuali.rice.krad.service.MaintenanceDocumentService;
048import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
049import org.kuali.rice.krad.util.GlobalVariables;
050import org.kuali.rice.krad.util.KRADConstants;
051import org.kuali.rice.krad.util.KRADPropertyConstants;
052import org.kuali.rice.krad.util.KRADUtils;
053import org.springframework.beans.factory.annotation.Required;
054
055/**
056 * Service implementation for the MaintenanceDocument structure. This is the
057 * default implementation, that is delivered with Kuali
058 *
059 * @author Kuali Rice Team (rice.collab@kuali.org)
060 */
061@TransactionalNoValidationExceptionRollback
062public class MaintenanceDocumentServiceImpl implements MaintenanceDocumentService {
063    private static final Logger LOG = Logger.getLogger(MaintenanceDocumentServiceImpl.class);
064
065    protected LegacyDataAdapter legacyDataAdapter;
066    protected DataObjectService dataObjectService;
067    protected DataObjectAuthorizationService dataObjectAuthorizationService;
068    protected DocumentService documentService;
069    protected DocumentDictionaryService documentDictionaryService;
070
071    /**
072     * @see org.kuali.rice.krad.service.MaintenanceDocumentService#setupNewMaintenanceDocument(java.lang.String,
073     *      java.lang.String, java.lang.String)
074     */
075    @Override
076        @SuppressWarnings("unchecked")
077    public MaintenanceDocument setupNewMaintenanceDocument(String objectClassName, String documentTypeName,
078            String maintenanceAction) {
079        if (StringUtils.isEmpty(objectClassName) && StringUtils.isEmpty(documentTypeName)) {
080            throw new IllegalArgumentException("Document type name or bo class not given!");
081        }
082
083        // get document type if not passed
084        if (StringUtils.isEmpty(documentTypeName)) {
085            try {
086                documentTypeName =
087                        getDocumentDictionaryService().getMaintenanceDocumentTypeName(Class.forName(objectClassName));
088            } catch (ClassNotFoundException e) {
089                throw new RuntimeException(e);
090            }
091
092            if (StringUtils.isEmpty(documentTypeName)) {
093                throw new RuntimeException(
094                        "documentTypeName is empty; does this Business Object have a maintenance document definition? " +
095                                objectClassName);
096            }
097        }
098
099        // check doc type allows new or copy if that action was requested
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        // get new document from service
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     * @see org.kuali.rice.krad.service.impl.MaintenanceDocumentServiceImpl#setupMaintenanceObject
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        // if action is delete, check that object can be deleted
133        if (KRADConstants.MAINTENANCE_DELETE_ACTION.equals(maintenanceAction))
134        {
135            checkMaintenanceActionAuthorization(document, document.getOldMaintainableObject(),
136                    maintenanceAction, requestParameters);
137        }
138
139        // if action is edit or copy first need to retrieve the old record
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               Copy with clearing objectId and version number in the parent and all nested objects if dataObject is an
148               instance of DataObjectBase or PersistableBusinessObject
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            // set object instance for editing
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                // Since this will be a new object, we also need to blank out the object ID and version number fields
169                // (if they exist).  If the object uses a different locking key or unique ID field, the blanking of
170                // these will need to be done in the Maintainable.processAfterCopy() method.
171                if(!dataObjectService.supports(oldDataObject.getClass())) {
172                    // If neither then use reflection to see if the object has setVersionNumber and setObjectId methods
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        // if new with existing we need to populate with passed in parameters
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     * For the edit and delete maintenance actions checks with the
210     * <code>BusinessObjectAuthorizationService</code> to check whether the
211     * action is allowed for the record data. In action is allowed invokes the
212     * custom processing hook on the <code>Maintainble</code>.
213     *
214     * @param document - document instance for the maintenance object
215     * @param oldBusinessObject - the old maintenance record
216     * @param maintenanceAction - type of maintenance action requested
217     * @param requestParameters - map of parameters from the request
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            // invoke custom processing method
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     * For the edit or copy actions retrieves the record that is to be
265     * maintained
266     *
267     * <p>
268     * Based on the persistence metadata for the maintenance object class
269     * retrieves the primary key values from the given request parameters map
270     * (if the class is persistable). With those key values attempts to find the
271     * record using the <code>LookupService</code>.
272     * </p>
273     *
274     * @param document - document instance for the maintenance object
275     * @param requestParameters - Map of parameters from the request
276     * @return Object the retrieved old object
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     * Clears the value of the primary key fields on the maintenance object
313     *
314     * @param maintenanceObject - document to clear the pk fields on
315     * @param dataObjectClass - class to use for retrieving primary key metadata
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     * Clears the value of the particular fields on the maintenance object
326     *
327     * @param maintenanceObject - document to clear the fields on
328     * @param dataObjectClass - class to use for retrieving list of fields to clear
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     * Clears the value of objects in nested objects on the maintenance object
350     *
351     * @param objectName -  name of the object which contains the field to be cleared
352     * @param maintenanceObject - Object to clear the fields on
353     * @param objectToClear - the object to be cleared on the nested object
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     * Based on the maintenance object class retrieves the key field names from
391     * the <code>BusinessObjectMetaDataService</code> (or alternatively from the
392     * request parameters), then retrieves any matching key value pairs from the
393     * request parameters
394     *
395     * @param requestParameters - map of parameters from the request
396     * @param dataObjectClass - class to use for checking security parameter restrictions
397     * @return Map<String, String> key value pairs
398     */
399    protected Map<String, String> buildKeyMapFromRequest(Map<String, String[]> requestParameters,
400            Class<?> dataObjectClass) {
401        List<String> keyFieldNames = null;
402
403        // translate request parameters
404        Map<String, String> parameters = KRADUtils.translateRequestParameterMap(requestParameters);
405
406        // are override keys listed in the request? If so, then those need to be
407        // our keys, not the primary key fields for the BO
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     * Looks for a special request parameters giving the names of the keys that
421     * should be retrieved from the request parameters and copied to the
422     * maintenance object
423     *
424     * @param parameters - map of parameters from the request
425     * @param oldBusinessObject - the old maintenance object
426     * @param oldMaintainableObject - the old maintainble object (used to get object class for
427     * security checks)
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            // TODO: make sure we are doing formatting here eventually
445            ObjectPropertyUtils.copyPropertiesToObject(parametersToCopy, oldBusinessObject);
446        }
447    }
448
449    /**
450     * @see org.kuali.rice.krad.service.MaintenanceDocumentService#getLockingDocumentId(org.kuali.rice.krad.maintenance.MaintenanceDocument)
451     */
452    @Override
453        public String getLockingDocumentId(MaintenanceDocument document) {
454        return getLockingDocumentId(document.getNewMaintainableObject(), document.getDocumentNumber());
455    }
456
457    /**
458     * @see org.kuali.rice.krad.service.MaintenanceDocumentService#getLockingDocumentId(org.kuali.rice.krad.maintenance.Maintainable,
459     *      java.lang.String)
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        // build the query criteria
480        List<Predicate> predicates = new ArrayList<Predicate>();
481        predicates.add(PredicateFactory.equal("lockingRepresentation", lockingRepresentation));
482
483        // if a docHeaderId is specified, then it will be excluded from the
484        // locking representation test.
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        // attempt to retrieve a document based off this criteria
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        // if a document was found, then there's already one out there pending,
501        // and we consider it 'locked' and we return the docnumber.
502        if (!results.isEmpty()) {
503            lockingDocNumber = results.get(0).getDocumentNumber();
504        }
505        return lockingDocNumber;
506    }
507
508    /**
509     * @see org.kuali.rice.krad.service.MaintenanceDocumentService#deleteLocks(String)
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     * @see org.kuali.rice.krad.service.MaintenanceDocumentService#storeLocks(java.util.List)
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}