View Javadoc
1   package org.kuali.rice.krad.data.provider.impl;
2   
3   import java.util.Set;
4   
5   import org.kuali.rice.core.api.criteria.QueryByCriteria;
6   import org.kuali.rice.core.api.criteria.QueryResults;
7   import org.kuali.rice.krad.data.CompoundKey;
8   import org.kuali.rice.krad.data.CopyOption;
9   import org.kuali.rice.krad.data.DataObjectService;
10  import org.kuali.rice.krad.data.DataObjectWrapper;
11  import org.kuali.rice.krad.data.PersistenceOption;
12  import org.kuali.rice.krad.data.metadata.DataObjectAttributeRelationship;
13  import org.kuali.rice.krad.data.metadata.DataObjectMetadata;
14  import org.kuali.rice.krad.data.metadata.DataObjectRelationship;
15  import org.kuali.rice.krad.data.metadata.MetadataChild;
16  import org.kuali.rice.krad.data.metadata.MetadataRepository;
17  import org.kuali.rice.krad.data.provider.PersistenceProvider;
18  import org.kuali.rice.krad.data.provider.ProviderRegistry;
19  import org.kuali.rice.krad.data.util.ReferenceLinker;
20  import org.springframework.beans.factory.annotation.Required;
21  import org.springframework.dao.IncorrectResultSizeDataAccessException;
22  
23  import com.google.common.collect.Sets;
24  
25  /**
26   * Created by sheiksalahudeenm on 10/10/14.
27   *
28   * Overridden for fixing the issue in KRAD Transaction document/JPA with composite primary key.
29   * Modified method name: reduceCompoundKey.
30   * Changes description : Commented the reduceCompoundKey method functionality. Just returning the same object which is given to this method.
31   */
32  public class ProviderBasedDataObjectService implements DataObjectService {
33  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
34  			.getLogger(ProviderBasedDataObjectService.class);
35  
36      /**
37       * The provider registry.
38       */
39      protected ProviderRegistry providerRegistry;
40  
41      /**
42       * The metadata repository.
43       */
44      protected MetadataRepository metadataRepository;
45  
46      /**
47       * The reference linker.
48       */
49      protected ReferenceLinker referenceLinker;
50  
51      /**
52       * {@inheritDoc}
53       */
54      @Override
55      public <T> T find(Class<T> type, Object id) {
56          return persistenceProviderForType(type).find(type, reduceCompoundKey(id));
57      }
58  
59      /**
60       * If the given id object is an instance of CompoundKey but there is only one entry in the key map, then just grab
61       * that single value and treat it as a single id.
62       *
63       * @param id the potentially CompoundKey to reduce
64       * @return the single value from the CompoundKey map if the given id is a CompoundKey with a single entry, otherwise
65       *         the original id that was passed in is returned
66       */
67      protected Object reduceCompoundKey(Object id) {
68          /* Modified by 'Sheik Salahudeen' for fixing the 'workflow document is null' issue in KRAD Transaction document with JPA.
69          * Commented the reduceCompoundKey method functionality. Just returning the same object which is given to this method */
70  
71           /*if (id instanceof CompoundKey) {
72              CompoundKey compoundKey = (CompoundKey)id;
73              if (compoundKey.getKeys().size() == 1) {
74                  id = compoundKey.getKeys().values().iterator().next();
75              }
76          }*/
77          return id;
78      }
79  
80      /**
81       * {@inheritDoc}
82       */
83      @Override
84      public <T> QueryResults<T> findMatching(Class<T> type, QueryByCriteria queryByCriteria) {
85          return persistenceProviderForType(type).findMatching(type, queryByCriteria);
86      }
87  
88      /**
89       * {@inheritDoc}
90       */
91      @Override
92      public <T> QueryResults<T> findAll(Class<T> type) {
93          return persistenceProviderForType(type).findAll(type);
94      }
95  
96      /**
97       * {@inheritDoc}
98       */
99      @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 }