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 org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.mo.common.active.MutableInactivatable;
020import org.kuali.rice.krad.bo.BusinessObject;
021import org.kuali.rice.krad.bo.DataObjectRelationship;
022import org.kuali.rice.krad.datadictionary.InactivationBlockingMetadata;
023import org.kuali.rice.krad.service.BusinessObjectService;
024import org.kuali.rice.krad.service.DataObjectMetaDataService;
025import org.kuali.rice.krad.service.InactivationBlockingDetectionService;
026import org.kuali.rice.krad.util.ObjectUtils;
027import org.springframework.transaction.annotation.Transactional;
028
029import java.util.ArrayList;
030import java.util.Collection;
031import java.util.HashMap;
032import java.util.Iterator;
033import java.util.Map;
034
035/**
036 * Performs checking of inactivation blocking 
037 * 
038 * @author Kuali Rice Team (rice.collab@kuali.org)
039 */
040@Transactional
041public class InactivationBlockingDetectionServiceImpl implements InactivationBlockingDetectionService {
042    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(InactivationBlockingDetectionServiceImpl.class);
043
044    protected DataObjectMetaDataService dataObjectMetaDataService;
045    protected BusinessObjectService businessObjectService;
046    
047    /**
048     * Note we are checking the active getting after retrieving potential blocking records instead of setting criteria on the
049         * active field. This is because some implementations of {@link org.kuali.rice.core.api.mo.common.active.MutableInactivatable} might not have the active field, for example
050         * instances of {@link org.kuali.rice.krad.bo.InactivatableFromTo}
051         * 
052     * @see org.kuali.rice.krad.service.InactivationBlockingDetectionService#listAllBlockerRecords(org.kuali.rice.krad.datadictionary.InactivationBlockingDefinition)
053     * @see org.kuali.rice.core.api.mo.common.active.MutableInactivatable
054     */
055    @SuppressWarnings("unchecked")
056        public Collection<BusinessObject> listAllBlockerRecords(BusinessObject blockedBo, InactivationBlockingMetadata inactivationBlockingMetadata) {
057                Collection<BusinessObject> blockingRecords = new ArrayList<BusinessObject>();
058
059                Map<String, String> queryMap = buildInactivationBlockerQueryMap(blockedBo, inactivationBlockingMetadata);
060                if (LOG.isDebugEnabled()) {
061                        LOG.debug("Checking for blocker records for object: " + blockedBo);
062                        LOG.debug("    With Metadata: " + inactivationBlockingMetadata);
063                        LOG.debug("    Resulting Query Map: " + queryMap);
064                }
065
066                if (queryMap != null) {
067                        Collection potentialBlockingRecords = businessObjectService.findMatching(
068                                        inactivationBlockingMetadata.getBlockingReferenceBusinessObjectClass(), queryMap);
069                        for (Iterator iterator = potentialBlockingRecords.iterator(); iterator.hasNext();) {
070                                MutableInactivatable businessObject = (MutableInactivatable) iterator.next();
071                                if (businessObject.isActive()) {
072                                        blockingRecords.add((BusinessObject) businessObject);
073                                }
074                        }
075                }
076
077                return blockingRecords;
078        }
079
080        /**
081         * Note we are checking the active getting after retrieving potential blocking records instead of setting criteria on the
082         * active field. This is because some implementations of {@link org.kuali.rice.core.api.mo.common.active.MutableInactivatable} might not have the active field, for example
083         * instances of {@link org.kuali.rice.krad.bo.InactivatableFromTo}
084         * 
085         * @see org.kuali.rice.krad.service.InactivationBlockingDetectionService#hasABlockingRecord(org.kuali.rice.krad.bo.BusinessObject,
086         *      org.kuali.rice.krad.datadictionary.InactivationBlockingMetadata)
087         * @see org.kuali.rice.core.api.mo.common.active.MutableInactivatable
088         */
089        public boolean hasABlockingRecord(BusinessObject blockedBo, InactivationBlockingMetadata inactivationBlockingMetadata) {
090                boolean hasBlockingRecord = false;
091
092                Map<String, String> queryMap = buildInactivationBlockerQueryMap(blockedBo, inactivationBlockingMetadata);
093                if (queryMap != null) {
094                        Collection potentialBlockingRecords = businessObjectService.findMatching(
095                                        inactivationBlockingMetadata.getBlockingReferenceBusinessObjectClass(), queryMap);
096                        for (Iterator iterator = potentialBlockingRecords.iterator(); iterator.hasNext();) {
097                                MutableInactivatable businessObject = (MutableInactivatable) iterator.next();
098                                if (businessObject.isActive()) {
099                                        hasBlockingRecord = true;
100                                        break;
101                                }
102                        }
103                }
104
105                // if queryMap were null, means that we couldn't perform a query, and hence, need to return false
106                return hasBlockingRecord;
107        }
108
109        protected Map<String, String> buildInactivationBlockerQueryMap(BusinessObject blockedBo, InactivationBlockingMetadata inactivationBlockingMetadata) {
110                BusinessObject blockingBo = (BusinessObject) ObjectUtils.createNewObjectFromClass(inactivationBlockingMetadata
111                                .getBlockingReferenceBusinessObjectClass());
112
113                DataObjectRelationship dataObjectRelationship = dataObjectMetaDataService
114                                .getDataObjectRelationship(blockingBo, blockedBo.getClass(),
115                        inactivationBlockingMetadata.getBlockedReferencePropertyName(), "", true, false, false);
116
117                // note, this method assumes that all PK fields of the blockedBo have a non-null and, for strings, non-blank values
118                if (dataObjectRelationship != null) {
119                        Map<String, String> parentToChildReferences = dataObjectRelationship.getParentToChildReferences();
120                        Map<String, String> queryMap = new HashMap<String, String>();
121                        for (Map.Entry<String, String> parentToChildReference : parentToChildReferences.entrySet()) {
122                                String fieldName = parentToChildReference.getKey();
123                                Object fieldValue = ObjectUtils.getPropertyValue(blockedBo, parentToChildReference.getValue());
124                                if (fieldValue != null && StringUtils.isNotBlank(fieldValue.toString())) {
125                                        queryMap.put(fieldName, fieldValue.toString());
126                                } else {
127                                        LOG.error("Found null value for foreign key field " + fieldName
128                                                        + " while building inactivation blocking query map.");
129                                        throw new RuntimeException("Found null value for foreign key field '" + fieldName
130                                                        + "' while building inactivation blocking query map.");
131                                }
132                        }
133
134                        return queryMap;
135                }
136
137                return null;
138        }
139    
140    public void setDataObjectMetaDataService(DataObjectMetaDataService dataObjectMetaDataService) {
141        this.dataObjectMetaDataService = dataObjectMetaDataService;
142    }
143
144    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
145        this.businessObjectService = businessObjectService;
146    }
147
148}