001/** 002 * Copyright 2005-2014 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.data.provider.impl; 017 018import java.util.Set; 019 020import org.kuali.rice.core.api.criteria.QueryByCriteria; 021import org.kuali.rice.core.api.criteria.QueryResults; 022import org.kuali.rice.krad.data.CompoundKey; 023import org.kuali.rice.krad.data.CopyOption; 024import org.kuali.rice.krad.data.DataObjectService; 025import org.kuali.rice.krad.data.DataObjectWrapper; 026import org.kuali.rice.krad.data.PersistenceOption; 027import org.kuali.rice.krad.data.metadata.DataObjectAttributeRelationship; 028import org.kuali.rice.krad.data.metadata.DataObjectMetadata; 029import org.kuali.rice.krad.data.metadata.DataObjectRelationship; 030import org.kuali.rice.krad.data.metadata.MetadataChild; 031import org.kuali.rice.krad.data.metadata.MetadataRepository; 032import org.kuali.rice.krad.data.provider.PersistenceProvider; 033import org.kuali.rice.krad.data.provider.ProviderRegistry; 034import org.kuali.rice.krad.data.util.ReferenceLinker; 035import org.springframework.beans.factory.annotation.Required; 036import org.springframework.dao.IncorrectResultSizeDataAccessException; 037 038import com.google.common.collect.Sets; 039 040/** 041 * DataObjectService implementation backed by the {@link ProviderRegistry}. 042 * 043 * @author Kuali Rice Team (rice.collab@kuali.org) 044 */ 045public class ProviderBasedDataObjectService implements DataObjectService { 046 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger 047 .getLogger(ProviderBasedDataObjectService.class); 048 049 /** 050 * The provider registry. 051 */ 052 protected ProviderRegistry providerRegistry; 053 054 /** 055 * The metadata repository. 056 */ 057 protected MetadataRepository metadataRepository; 058 059 /** 060 * The reference linker. 061 */ 062 protected ReferenceLinker referenceLinker; 063 064 /** 065 * {@inheritDoc} 066 */ 067 @Override 068 public <T> T find(Class<T> type, Object id) { 069 return persistenceProviderForType(type).find(type, reduceCompoundKey(id)); 070 } 071 072 /** 073 * If the given id object is an instance of CompoundKey but there is only one entry in the key map, then just grab 074 * that single value and treat it as a single id. 075 * 076 * @param id the potentially CompoundKey to reduce 077 * 078 * @return the single value from the CompoundKey map if the given id is a CompoundKey with a single entry, otherwise 079 * the original id that was passed in is returned 080 */ 081 protected Object reduceCompoundKey(Object id) { 082 if (id instanceof CompoundKey) { 083 CompoundKey compoundKey = (CompoundKey)id; 084 if (compoundKey.getKeys().size() == 1) { 085 id = compoundKey.getKeys().values().iterator().next(); 086 } 087 } 088 return id; 089 } 090 091 /** 092 * {@inheritDoc} 093 */ 094 @Override 095 public <T> QueryResults<T> findMatching(Class<T> type, QueryByCriteria queryByCriteria) { 096 return persistenceProviderForType(type).findMatching(type, queryByCriteria); 097 } 098 099 /** 100 * {@inheritDoc} 101 */ 102 @Override 103 public <T> QueryResults<T> findAll(Class<T> type) { 104 return persistenceProviderForType(type).findAll(type); 105 } 106 107 /** 108 * {@inheritDoc} 109 */ 110 @Override 111 public <T> T findUnique(Class<T> type, QueryByCriteria queryByCriteria) { 112 QueryResults<T> results = findMatching(type, queryByCriteria); 113 if (results.getResults().isEmpty()) { 114 return null; 115 } else if (results.getResults().size() > 1) { 116 throw new IncorrectResultSizeDataAccessException("Attempted to find single result but found more than " 117 + "one for class " + type + " and criteria " + queryByCriteria, 1, results.getResults().size()); 118 } else { 119 return results.getResults().get(0); 120 } 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override 127 public void delete(Object dataObject) { 128 persistenceProviderForObject(dataObject).delete(dataObject); 129 } 130 131 /** 132 * {@inheritDoc} 133 */ 134 @Override 135 public <T> void deleteMatching(Class<T> type, QueryByCriteria queryByCriteria) { 136 persistenceProviderForType(type).deleteMatching(type, queryByCriteria); 137 } 138 139 /** 140 * 141 *{@inheritDoc} 142 */ 143 @Override 144 public <T> void deleteAll(Class<T> type) { 145 persistenceProviderForType(type).deleteAll(type); 146 } 147 148 /** 149 * {@inheritDoc} 150 */ 151 @Override 152 public <T> T save(T dataObject, PersistenceOption... options) { 153 Set<PersistenceOption> optionSet = Sets.newHashSet(options); 154 pushOneToOneKeysToChildObjects(dataObject); 155 T saved = persistenceProviderForObject(dataObject).save(dataObject, options); 156 if (optionSet.contains(PersistenceOption.LINK_KEYS)) { 157 DataObjectWrapper<T> wrapper = wrap(saved); 158 wrapper.linkForeignKeys(true); 159 } 160 return saved; 161 } 162 163 protected void pushOneToOneKeysToChildObjects(Object dataObject) { 164 DataObjectWrapper<Object> wrappedParent = wrap(dataObject); 165 if (wrappedParent.getMetadata() == null) { 166 return; 167 } 168 // Loop over all relationships 169 for (DataObjectRelationship rel : wrappedParent.getMetadata().getRelationships()) { 170 // look for those which are part of this object exclusively 171 if (rel.isSavedWithParent() && rel.isDeletedWithParent()) { 172 Object child = wrappedParent.getPropertyValueNullSafe(rel.getName()); 173 // if the child is null, just skip 174 if (child == null) { 175 continue; 176 } 177 DataObjectWrapper<Object> wrappedChild = wrap(child); 178 // REMOVED THIS FOR NOW - THE ATTRIBUTES DON'T EXIST IN THIS DIRECTION 179 // loop over the attributes, setting them on the child object 180 // for (DataObjectAttributeRelationship attr : rel.getAttributeRelationships()) { 181 // wrappedChild.setPropertyValue(attr.getChildAttributeName(), 182 // dataObjectWrapper.getPropertyValueNullSafe(attr.getParentAttributeName())); 183 // } 184 // inverse relationship - if it exists, add the parent object in 185 // the applicable property 186 MetadataChild inverseRelationship = rel.getInverseRelationship(); 187 if (inverseRelationship != null && inverseRelationship instanceof DataObjectRelationship) { 188 try { 189 wrappedChild.setPropertyValue(inverseRelationship.getName(), dataObject); 190 for (DataObjectAttributeRelationship attr : inverseRelationship.getAttributeRelationships()) { 191 // note the reversal of child and parent - remember this is the *child's* 192 // relationship with the parent 193 // and like many children, the they they are in charge 194 wrappedChild.setPropertyValue(attr.getParentAttributeName(), 195 wrappedParent.getPropertyValueNullSafe(attr.getChildAttributeName())); 196 } 197 } catch (Exception ex) { 198 LOG.warn("Unable to set 1:1 child keys. Persistance of child object may not be correct. Parent Object.property: " 199 + dataObject.getClass().getName() 200 + "." 201 + rel.getName() 202 + " / Child Type: " 203 + child.getClass().getName(), ex); 204 } 205 } 206 } 207 } 208 209 } 210 211 /** 212 * {@inheritDoc} 213 */ 214 @Override 215 public MetadataRepository getMetadataRepository() { 216 return metadataRepository; 217 } 218 219 /** 220 * {@inheritDoc} 221 */ 222 @Override 223 public <T> T copyInstance(T dataObject, CopyOption... options) { 224 return persistenceProviderForObject(dataObject).copyInstance(dataObject, options); 225 } 226 227 /** 228 * {@inheritDoc} 229 */ 230 @Override 231 public <T> DataObjectWrapper<T> wrap(T dataObject) { 232 if (dataObject == null) { 233 throw new IllegalArgumentException("data object was null"); 234 } 235 DataObjectMetadata metadata = getMetadataRepository().getMetadata(dataObject.getClass()); 236 // Checking for metadata and failing here. Otherwise a null gets stored in the wrapper 237 // and most later operations on the object will fail with an NPE. 238 if (metadata == null) { 239 LOG.warn("Non KRAD Data object passed - no metadata found for: " + dataObject.getClass()); 240 // throw new IllegalArgumentException("Non KRAD Data object passed - no metadata found for: " + 241 // dataObject.getClass()); 242 } 243 return new DataObjectWrapperImpl<T>(dataObject, metadata, this, referenceLinker); 244 } 245 246 /** 247 * {@inheritDoc} 248 */ 249 @Override 250 public <T> boolean supports(Class<T> type) { 251 return providerRegistry.getPersistenceProvider(type) != null; 252 } 253 254 /** 255 * Gets the PersistenceProvider returned by the ProviderRegistry for the given type. 256 * 257 * @param type the type for which to get the provider. 258 * 259 * @return the PersistenceProvider returned by the ProviderRegistry for the given type. 260 * 261 * @throws RuntimeException if not PersistenceProvider handles given type. 262 */ 263 protected PersistenceProvider persistenceProviderForType(Class<?> type) { 264 PersistenceProvider provider = providerRegistry.getPersistenceProvider(type); 265 if (provider == null) { 266 throw new RuntimeException("No PersistenceProvider handles type: " + type); 267 } 268 return provider; 269 } 270 271 /** 272 * Gets the PersistenceProvider returned by the ProviderRegistry for the given object. 273 * 274 * @param object the object for which to get the provider. 275 * 276 * @return the PersistenceProvider returned by the ProviderRegistry for the given object. 277 * 278 * @throws RuntimeException if not PersistenceProvider handles given type. 279 * @throws IllegalArgumentException if null object passed in. 280 */ 281 protected PersistenceProvider persistenceProviderForObject(Object object) { 282 if (object == null) { 283 throw new IllegalArgumentException("data object was null"); 284 } 285 return persistenceProviderForType(object.getClass()); 286 } 287 288 /** 289 * {@inheritDoc} 290 */ 291 @Override 292 public void flush(Class<?> type){ 293 PersistenceProvider persistenceProvider = persistenceProviderForType(type); 294 if (persistenceProvider == null) { 295 throw new RuntimeException("No PersistenceProvider handles type: " + type); 296 } 297 persistenceProvider.flush(type); 298 } 299 300 /** 301 * Setter for the provider registry. 302 * 303 * @param providerRegistry the provider registry to set. 304 */ 305 @Required 306 public void setProviderRegistry(ProviderRegistry providerRegistry) { 307 this.providerRegistry = providerRegistry; 308 } 309 310 /** 311 * Setter for the metadata repository. 312 * 313 * @param metadataRepository the metadata repository to set. 314 */ 315 @Required 316 public void setMetadataRepository(MetadataRepository metadataRepository) { 317 this.metadataRepository = metadataRepository; 318 } 319 320 /** 321 * Gets the reference linker. 322 * 323 * @return the reference linker. 324 */ 325 public ReferenceLinker getReferenceLinker() { 326 return referenceLinker; 327 } 328 329 /** 330 * Setter for the reference linker. 331 * 332 * @param referenceLinker the reference linker to set. 333 */ 334 @Required 335 public void setReferenceLinker(ReferenceLinker referenceLinker) { 336 this.referenceLinker = referenceLinker; 337 } 338 339 /** 340 * Defines a very basic implementation for {@link DataObjectWrapperBase}. 341 * @param <T> the type of the data object to wrap. 342 */ 343 private static final class DataObjectWrapperImpl<T> extends DataObjectWrapperBase<T> { 344 345 /** 346 * Creates a data object wrapper. 347 * 348 * @param dataObject the data object to wrap. 349 * @param metadata the metadata of the data object. 350 * @param dataObjectService the data object service to use. 351 * @param referenceLinker the reference linker implementation. 352 */ 353 private DataObjectWrapperImpl(T dataObject, DataObjectMetadata metadata, DataObjectService dataObjectService, 354 ReferenceLinker referenceLinker) { 355 super(dataObject, metadata, dataObjectService, referenceLinker); 356 } 357 } 358 359}