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