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.criteria.Predicate; 020import org.kuali.rice.core.api.criteria.PredicateFactory; 021import org.kuali.rice.core.api.criteria.QueryByCriteria; 022import org.kuali.rice.core.api.criteria.QueryResults; 023import org.kuali.rice.core.api.mo.common.active.Inactivatable; 024import org.kuali.rice.core.api.mo.common.active.MutableInactivatable; 025import org.kuali.rice.krad.bo.BusinessObject; 026import org.kuali.rice.krad.data.DataObjectService; 027import org.kuali.rice.krad.data.DataObjectWrapper; 028import org.kuali.rice.krad.data.KradDataServiceLocator; 029import org.kuali.rice.krad.data.metadata.DataObjectAttributeRelationship; 030import org.kuali.rice.krad.data.metadata.DataObjectMetadata; 031import org.kuali.rice.krad.data.metadata.DataObjectRelationship; 032import org.kuali.rice.krad.datadictionary.InactivationBlockingMetadata; 033import org.kuali.rice.krad.datadictionary.RelationshipDefinition; 034import org.kuali.rice.krad.service.DataObjectMetaDataService; 035import org.kuali.rice.krad.service.InactivationBlockingDetectionService; 036import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 037import org.kuali.rice.krad.service.LegacyDataAdapter; 038import org.kuali.rice.krad.util.KRADUtils; 039import org.kuali.rice.krad.util.LegacyUtils; 040import org.springframework.transaction.annotation.Transactional; 041 042import java.util.ArrayList; 043import java.util.Collection; 044import java.util.HashMap; 045import java.util.Iterator; 046import java.util.List; 047import java.util.Map; 048 049/** 050 * Performs checking of inactivation blocking 051 * 052 * @author Kuali Rice Team (rice.collab@kuali.org) 053 */ 054@Transactional 055public class InactivationBlockingDetectionServiceImpl implements InactivationBlockingDetectionService { 056 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(InactivationBlockingDetectionServiceImpl.class); 057 058 protected DataObjectMetaDataService dataObjectMetaDataService; 059 private volatile DataObjectService dataObjectService; 060 protected LegacyDataAdapter legacyDataAdapter; 061 062 /** 063 * Note we are checking the active getting after retrieving potential blocking records instead of setting criteria on the 064 * 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 065 * instances of {@link org.kuali.rice.krad.bo.InactivatableFromTo} 066 * 067 * @see org.kuali.rice.krad.service.InactivationBlockingDetectionService#listAllBlockerRecords(org.kuali.rice.krad.bo.BusinessObject, org.kuali.rice.krad.datadictionary.InactivationBlockingMetadata) 068 * @see org.kuali.rice.core.api.mo.common.active.MutableInactivatable 069 */ 070 @SuppressWarnings("unchecked") 071 @Deprecated 072 @Override 073 public Collection<BusinessObject> listAllBlockerRecords(BusinessObject blockedBo, InactivationBlockingMetadata inactivationBlockingMetadata) { 074 Collection<BusinessObject> blockingRecords = new ArrayList<BusinessObject>(); 075 076 Map<String, String> queryMap = buildInactivationBlockerQueryMap(blockedBo, inactivationBlockingMetadata); 077 if (LOG.isDebugEnabled()) { 078 LOG.debug("Checking for blocker records for object: " + blockedBo); 079 LOG.debug(" With Metadata: " + inactivationBlockingMetadata); 080 LOG.debug(" Resulting Query Map: " + queryMap); 081 } 082 083 if (queryMap != null) { 084 Collection potentialBlockingRecords = legacyDataAdapter.findMatching( 085 inactivationBlockingMetadata.getBlockingReferenceBusinessObjectClass(), queryMap); 086 for (Iterator iterator = potentialBlockingRecords.iterator(); iterator.hasNext();) { 087 MutableInactivatable businessObject = (MutableInactivatable) iterator.next(); 088 if (businessObject.isActive()) { 089 blockingRecords.add((BusinessObject) businessObject); 090 } 091 } 092 } 093 094 return blockingRecords; 095 } 096 097 /** 098 * Note we are checking the active getting after retrieving potential blocking records instead of setting criteria on the 099 * 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 100 * instances of {@link org.kuali.rice.krad.bo.InactivatableFromTo} 101 * 102 * @see org.kuali.rice.krad.service.InactivationBlockingDetectionService#hasABlockingRecord(org.kuali.rice.krad.bo.BusinessObject, 103 * org.kuali.rice.krad.datadictionary.InactivationBlockingMetadata) 104 * @see org.kuali.rice.core.api.mo.common.active.MutableInactivatable 105 */ 106 @Deprecated 107 @Override 108 public boolean hasABlockingRecord(BusinessObject blockedBo, InactivationBlockingMetadata inactivationBlockingMetadata) { 109 boolean hasBlockingRecord = false; 110 111 Map<String, String> queryMap = buildInactivationBlockerQueryMap(blockedBo, inactivationBlockingMetadata); 112 if (queryMap != null) { 113 Collection potentialBlockingRecords = legacyDataAdapter.findMatching( 114 inactivationBlockingMetadata.getBlockingReferenceBusinessObjectClass(), queryMap); 115 for (Iterator iterator = potentialBlockingRecords.iterator(); iterator.hasNext();) { 116 MutableInactivatable businessObject = (MutableInactivatable) iterator.next(); 117 if (businessObject.isActive()) { 118 hasBlockingRecord = true; 119 break; 120 } 121 } 122 } 123 124 // if queryMap were null, means that we couldn't perform a query, and hence, need to return false 125 return hasBlockingRecord; 126 } 127 128 @Deprecated 129 protected Map<String, String> buildInactivationBlockerQueryMap(BusinessObject blockedBo, InactivationBlockingMetadata inactivationBlockingMetadata) { 130 BusinessObject blockingBo = (BusinessObject) KRADUtils.createNewObjectFromClass( 131 inactivationBlockingMetadata.getBlockingReferenceBusinessObjectClass()); 132 133 org.kuali.rice.krad.bo.DataObjectRelationship dataObjectRelationship = legacyDataAdapter 134 .getDataObjectRelationship(blockingBo, blockedBo.getClass(), 135 inactivationBlockingMetadata.getBlockedReferencePropertyName(), "", true, false, false); 136 137 RelationshipDefinition relationshipDefinition = KRADServiceLocatorWeb.getLegacyDataAdapter().getDictionaryRelationship(blockedBo.getClass(),inactivationBlockingMetadata.getBlockedReferencePropertyName()); 138 139 // note, this method assumes that all PK fields of the blockedBo have a non-null and, for strings, non-blank values 140 if (dataObjectRelationship != null) { 141 Map<String, String> parentToChildReferences = dataObjectRelationship.getParentToChildReferences(); 142 Map<String, String> queryMap = new HashMap<String, String>(); 143 for (Map.Entry<String, String> parentToChildReference : parentToChildReferences.entrySet()) { 144 String fieldName = parentToChildReference.getKey(); 145 Object fieldValue = KradDataServiceLocator.getDataObjectService().wrap(blockedBo).getPropertyValueNullSafe(parentToChildReference.getValue()); 146 if (fieldValue != null && StringUtils.isNotBlank(fieldValue.toString())) { 147 queryMap.put(fieldName, fieldValue.toString()); 148 } else { 149 LOG.error("Found null value for foreign key field " + fieldName 150 + " while building inactivation blocking query map."); 151 throw new RuntimeException("Found null value for foreign key field '" + fieldName 152 + "' while building inactivation blocking query map."); 153 } 154 } 155 156 return queryMap; 157 } 158 159 return null; 160 } 161 162 /** 163 * Implementation which calls the legacy {@link #hasABlockingRecord(org.kuali.rice.krad.bo.BusinessObject, org.kuali.rice.krad.datadictionary.InactivationBlockingMetadata)} 164 * if the given data object is a legacy object. Calls new code to make the equivalent check if the given object is 165 * non-legacy. 166 */ 167 @Override 168 public boolean detectBlockingRecord(Object dataObject, InactivationBlockingMetadata inactivationBlockingMetadata) { 169 if (LegacyUtils.useLegacyForObject(dataObject)) { 170 return hasABlockingRecord((BusinessObject)dataObject, inactivationBlockingMetadata); 171 } 172 QueryByCriteria criteria = buildInactivationBlockerCriteria(dataObject, inactivationBlockingMetadata); 173 if (criteria != null) { 174 Class<?> blockingType = inactivationBlockingMetadata.getBlockingDataObjectClass(); 175 QueryResults<?> potentialBlockingRecords = getDataObjectService().findMatching(blockingType, criteria); 176 for (Object result : potentialBlockingRecords.getResults()) { 177 if (!(result instanceof Inactivatable)) { 178 throw new IllegalStateException("Blocking records must implement Inactivatable, but encountered one which does not: " + result); 179 } 180 Inactivatable inactivatable = (Inactivatable)result; 181 if (inactivatable.isActive()) { 182 return true; 183 } 184 } 185 } 186 // if criteria is null, means that we couldn't perform a query, and hence, need to return false 187 return false; 188 } 189 190 /** 191 * Implementation which calls the legacy {@link #listAllBlockerRecords(org.kuali.rice.krad.bo.BusinessObject, org.kuali.rice.krad.datadictionary.InactivationBlockingMetadata)} 192 * if the given data object is a legacy object. Calls new code to make the equivalent check if the given object is 193 * non-legacy. 194 */ 195 @Override 196 public Collection<?> detectAllBlockingRecords(Object dataObject, 197 InactivationBlockingMetadata inactivationBlockingMetadata) { 198 if (LegacyUtils.useLegacyForObject(dataObject)) { 199 return listAllBlockerRecords((BusinessObject) dataObject, inactivationBlockingMetadata); 200 } 201 List<Object> blockingRecords = new ArrayList<Object>(); 202 203 QueryByCriteria criteria = buildInactivationBlockerCriteria(dataObject, inactivationBlockingMetadata); 204 if (LOG.isDebugEnabled()) { 205 LOG.debug("Checking for blocker records for object: " + dataObject); 206 LOG.debug(" With Metadata: " + inactivationBlockingMetadata); 207 LOG.debug(" Resulting QueryByCriteria: " + criteria); 208 } 209 210 if (criteria != null) { 211 Class<?> blockingType = inactivationBlockingMetadata.getBlockingDataObjectClass(); 212 QueryResults<?> potentialBlockingRecords = getDataObjectService().findMatching(blockingType, criteria); 213 for (Object result : potentialBlockingRecords.getResults()) { 214 if (!(result instanceof Inactivatable)) { 215 throw new IllegalStateException("Blocking records must implement Inactivatable, but encountered one which does not: " + result); 216 } 217 Inactivatable inactivatable = (Inactivatable)result; 218 if (inactivatable.isActive()) { 219 blockingRecords.add(result); 220 } 221 } 222 } 223 224 return blockingRecords; 225 } 226 227 protected QueryByCriteria buildInactivationBlockerCriteria(Object blockedObject, InactivationBlockingMetadata inactivationBlockingMetadata) { 228 DataObjectMetadata blockingObjectMetadata = 229 getDataObjectService().getMetadataRepository().getMetadata(inactivationBlockingMetadata.getBlockingDataObjectClass()); 230 DataObjectRelationship dataObjectRelationship = 231 blockingObjectMetadata.getRelationship(inactivationBlockingMetadata.getBlockedAttributeName()); 232 233 // note, this method assumes that all PK fields of the blockedBo have a non-null and, for strings, non-blank values 234 if (dataObjectRelationship != null && !dataObjectRelationship.getAttributeRelationships().isEmpty()) { 235 DataObjectWrapper<?> wrap = getDataObjectService().wrap(blockedObject); 236 List<Predicate> predicates = new ArrayList<Predicate>(); 237 for (DataObjectAttributeRelationship relationship : dataObjectRelationship.getAttributeRelationships()) { 238 String fieldName = relationship.getParentAttributeName(); 239 Object fieldValue = wrap.getPropertyValue(relationship.getChildAttributeName()); 240 if (fieldValue != null && StringUtils.isNotBlank(fieldValue.toString())) { 241 predicates.add(PredicateFactory.equal(fieldName, fieldValue)); 242 } 243 } 244 return QueryByCriteria.Builder.fromPredicates(predicates.toArray(new Predicate[predicates.size()])); 245 } 246 return null; 247 } 248 249 250 public void setDataObjectMetaDataService(DataObjectMetaDataService dataObjectMetaDataService) { 251 this.dataObjectMetaDataService = dataObjectMetaDataService; 252 } 253 254 public void setLegacyDataAdapter(LegacyDataAdapter legacyDataAdapter) { 255 this.legacyDataAdapter = legacyDataAdapter; 256 } 257 258 public DataObjectService getDataObjectService() { 259 if (dataObjectService == null) { 260 dataObjectService = KradDataServiceLocator.getDataObjectService(); 261 } 262 return dataObjectService; 263 } 264 265 public void setDataObjectService(DataObjectService dataObjectService) { 266 this.dataObjectService = dataObjectService; 267 } 268}