View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.criteria.Predicate;
20  import org.kuali.rice.core.api.criteria.PredicateFactory;
21  import org.kuali.rice.core.api.criteria.QueryByCriteria;
22  import org.kuali.rice.core.api.criteria.QueryResults;
23  import org.kuali.rice.core.api.mo.common.active.Inactivatable;
24  import org.kuali.rice.core.api.mo.common.active.MutableInactivatable;
25  import org.kuali.rice.krad.bo.BusinessObject;
26  import org.kuali.rice.krad.data.DataObjectService;
27  import org.kuali.rice.krad.data.DataObjectWrapper;
28  import org.kuali.rice.krad.data.KradDataServiceLocator;
29  import org.kuali.rice.krad.data.metadata.DataObjectAttributeRelationship;
30  import org.kuali.rice.krad.data.metadata.DataObjectMetadata;
31  import org.kuali.rice.krad.data.metadata.DataObjectRelationship;
32  import org.kuali.rice.krad.datadictionary.InactivationBlockingMetadata;
33  import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
34  import org.kuali.rice.krad.service.DataObjectMetaDataService;
35  import org.kuali.rice.krad.service.InactivationBlockingDetectionService;
36  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
37  import org.kuali.rice.krad.service.LegacyDataAdapter;
38  import org.kuali.rice.krad.util.KRADUtils;
39  import org.kuali.rice.krad.util.LegacyUtils;
40  import org.springframework.transaction.annotation.Transactional;
41  
42  import java.util.ArrayList;
43  import java.util.Collection;
44  import java.util.HashMap;
45  import java.util.Iterator;
46  import java.util.List;
47  import java.util.Map;
48  
49  /**
50   * Performs checking of inactivation blocking 
51   * 
52   * @author Kuali Rice Team (rice.collab@kuali.org)
53   */
54  @Transactional
55  public class InactivationBlockingDetectionServiceImpl implements InactivationBlockingDetectionService {
56      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(InactivationBlockingDetectionServiceImpl.class);
57  
58      protected DataObjectMetaDataService dataObjectMetaDataService;
59      private volatile DataObjectService dataObjectService;
60      protected LegacyDataAdapter legacyDataAdapter;
61      
62      /**
63       * Note we are checking the active getting after retrieving potential blocking records instead of setting criteria on the
64  	 * 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
65  	 * instances of {@link org.kuali.rice.krad.bo.InactivatableFromTo}
66  	 * 
67       * @see org.kuali.rice.krad.service.InactivationBlockingDetectionService#listAllBlockerRecords(org.kuali.rice.krad.bo.BusinessObject, org.kuali.rice.krad.datadictionary.InactivationBlockingMetadata)
68       * @see org.kuali.rice.core.api.mo.common.active.MutableInactivatable
69       */
70      @SuppressWarnings("unchecked")
71      @Deprecated
72      @Override
73  	public Collection<BusinessObject> listAllBlockerRecords(BusinessObject blockedBo, InactivationBlockingMetadata inactivationBlockingMetadata) {
74  		Collection<BusinessObject> blockingRecords = new ArrayList<BusinessObject>();
75  
76  		Map<String, String> queryMap = buildInactivationBlockerQueryMap(blockedBo, inactivationBlockingMetadata);
77  		if (LOG.isDebugEnabled()) {
78  			LOG.debug("Checking for blocker records for object: " + blockedBo);
79  			LOG.debug("    With Metadata: " + inactivationBlockingMetadata);
80  			LOG.debug("    Resulting Query Map: " + queryMap);
81  		}
82  
83  		if (queryMap != null) {
84  			Collection potentialBlockingRecords = legacyDataAdapter.findMatching(
85  					inactivationBlockingMetadata.getBlockingReferenceBusinessObjectClass(), queryMap);
86  			for (Iterator iterator = potentialBlockingRecords.iterator(); iterator.hasNext();) {
87  				MutableInactivatable businessObject = (MutableInactivatable) iterator.next();
88  				if (businessObject.isActive()) {
89  					blockingRecords.add((BusinessObject) businessObject);
90  				}
91  			}
92  		}
93  
94  		return blockingRecords;
95  	}
96  
97  	/**
98  	 * Note we are checking the active getting after retrieving potential blocking records instead of setting criteria on the
99  	 * 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 }