View Javadoc
1   /**
2    * Copyright 2005-2014 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.data.provider.impl;
17  
18  import com.google.common.collect.Sets;
19  import org.kuali.rice.core.api.criteria.LookupCustomizer;
20  import org.kuali.rice.core.api.criteria.QueryByCriteria;
21  import org.kuali.rice.core.api.criteria.QueryResults;
22  import org.kuali.rice.krad.data.CompoundKey;
23  import org.kuali.rice.krad.data.DataObjectService;
24  import org.kuali.rice.krad.data.DataObjectWrapper;
25  import org.kuali.rice.krad.data.PersistenceOption;
26  import org.kuali.rice.krad.data.metadata.DataObjectMetadata;
27  import org.kuali.rice.krad.data.metadata.MetadataRepository;
28  import org.kuali.rice.krad.data.provider.PersistenceProvider;
29  import org.kuali.rice.krad.data.provider.ProviderRegistry;
30  import org.kuali.rice.krad.data.provider.util.ReferenceLinker;
31  import org.springframework.beans.factory.annotation.Required;
32  import org.springframework.dao.IncorrectResultSizeDataAccessException;
33  
34  import java.util.Set;
35  
36  /**
37   * DataObjectService implementation backed by the {@link ProviderRegistry}.
38   *
39   * @author Kuali Rice Team (rice.collab@kuali.org)
40   */
41  public class ProviderBasedDataObjectService implements DataObjectService {
42  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
43  			.getLogger(ProviderBasedDataObjectService.class);
44  
45  	protected ProviderRegistry providerRegistry;
46  	protected MetadataRepository metadataRepository;
47      protected ReferenceLinker referenceLinker;
48  
49      @Override
50      public <T> T find(Class<T> type, Object id) {
51          return persistenceProviderForType(type).find(type, reduceCompoundKey(id));
52      }
53  
54      /**
55       * If the given id object is an instance of CompoundKey but there is only one entry in the key map, then just grab
56       * that single value and treat it as a single id.
57       *
58       * @param id the potentially CompoundKey to reduce
59       * @return the single value from the CompoundKey map if the given id is a CompoundKey with a single entry, otherwise
60       *         the original id that was passed in is returned
61       */
62      protected Object reduceCompoundKey(Object id) {
63          if (id instanceof CompoundKey) {
64              CompoundKey compoundKey = (CompoundKey)id;
65              if (compoundKey.getKeys().size() == 1) {
66                  id = compoundKey.getKeys().values().iterator().next();
67              }
68          }
69          return id;
70      }
71  
72      @Override
73      public <T> QueryResults<T> findMatching(Class<T> type, QueryByCriteria queryByCriteria) {
74          return persistenceProviderForType(type).findMatching(type, queryByCriteria);
75      }
76  
77      @Override
78      public <T> QueryResults<T> findMatching(Class<T> type, QueryByCriteria queryByCriteria, LookupCustomizer<T> lookupCustomizer) {
79          return persistenceProviderForType(type).findMatching(type, queryByCriteria, lookupCustomizer);
80      }
81  
82      @Override
83      public <T> T findUnique(Class<T> type, QueryByCriteria queryByCriteria) {
84          QueryResults<T> results = findMatching(type, queryByCriteria);
85          if (results.getResults().isEmpty()) {
86              return null;
87          } else if (results.getResults().size() > 1) {
88              throw new IncorrectResultSizeDataAccessException("Attempted to find single result but found more than "
89                      + "one for class " + type + " and criteria " + queryByCriteria, 1, results.getResults().size());
90          } else {
91              return results.getResults().get(0);
92          }
93      }
94  
95      @Override
96      public void delete(Object dataObject) {
97          persistenceProviderForObject(dataObject).delete(dataObject);
98      }
99  
100     @Override
101     public <T> void deleteMatching(Class<T> type, QueryByCriteria queryByCriteria) {
102         QueryResults<T> results = findMatching(type, queryByCriteria);
103         for (T result: results.getResults()) {
104             delete(result);
105         }
106     }
107 
108     @Override
109 	public <T> T save(T dataObject, PersistenceOption... options) {
110         Set<PersistenceOption> optionSet = Sets.newHashSet(options);
111         T saved = persistenceProviderForObject(dataObject).save(dataObject, options);
112         if (optionSet.contains(PersistenceOption.LINK_KEYS)) {
113             DataObjectWrapper<T> wrapper = wrap(saved);
114             wrapper.linkForeignKeys(true);
115         }
116         return saved;
117     }
118 
119     @Override
120     public MetadataRepository getMetadataRepository() {
121         return metadataRepository;
122     }
123 
124     @Override
125     public <T> T copyInstance(T dataObject) {
126         return persistenceProviderForObject(dataObject).copyInstance(dataObject);
127     }
128 
129     @Override
130     public <T> DataObjectWrapper<T> wrap(T dataObject) {
131         if (dataObject == null) {
132             throw new IllegalArgumentException("data object was null");
133         }
134 		DataObjectMetadata metadata = getMetadataRepository().getMetadata(dataObject.getClass());
135 		// Checking for metadata and failing here. Otherwise a null gets stored in the wrapper
136 		// and most later operations on the object will fail with an NPE.
137 		if (metadata == null) {
138 			LOG.warn("Non KRAD Data object passed - no metadata found for: " + dataObject.getClass());
139 			// throw new IllegalArgumentException("Non KRAD Data object passed - no metadata found for: " +
140 			// dataObject.getClass());
141 		}
142         return new DataObjectWrapperImpl<T>(dataObject, metadata, this, referenceLinker);
143     }
144 
145     @Override
146     public <T> boolean supports(Class<T> type) {
147         return providerRegistry.getPersistenceProvider(type) != null;
148     }
149 
150     /**
151      * @return PersistenceProvider returned by the ProviderRegistry for the given type
152      * @throws RuntimeException if not PersistenceProvider handles given type
153      */
154     protected PersistenceProvider persistenceProviderForType(Class<?> type) {
155         PersistenceProvider provider = providerRegistry.getPersistenceProvider(type);
156         if (provider == null) {
157             throw new RuntimeException("No PersistenceProvider handles type: " + type);
158         }
159         return provider;
160     }
161 
162     /**
163 	 * @return PersistenceProvider returned by the ProviderRegistry for the given object
164 	 * @throws RuntimeException
165 	 *             if not PersistenceProvider handles given type
166 	 * @throws IllegalArgumentException
167 	 *             if null object passed in
168 	 */
169     protected PersistenceProvider persistenceProviderForObject(Object object) {
170 		if (object == null) {
171 			throw new IllegalArgumentException("data object was null");
172 		}
173 		return persistenceProviderForType(object.getClass());
174     }
175 
176     @Override
177     public void flush(Class<?> type){
178         PersistenceProvider persistenceProvider = persistenceProviderForType(type);
179         if (persistenceProvider == null) {
180             throw new RuntimeException("No PersistenceProvider handles type: " + type);
181         }
182         persistenceProvider.flush(type);
183     }
184 
185     @Required
186     public void setProviderRegistry(ProviderRegistry providerRegistry) {
187         this.providerRegistry = providerRegistry;
188     }
189 
190     @Required
191     public void setMetadataRepository(MetadataRepository metadataRepository) {
192         this.metadataRepository = metadataRepository;
193     }
194 
195     public ReferenceLinker getReferenceLinker() {
196         return referenceLinker;
197     }
198 
199     @Required
200     public void setReferenceLinker(ReferenceLinker referenceLinker) {
201         this.referenceLinker = referenceLinker;
202     }
203 
204     private static final class DataObjectWrapperImpl<T> extends DataObjectWrapperBase<T> {
205         private DataObjectWrapperImpl(T dataObject, DataObjectMetadata metadata, DataObjectService dataObjectService,
206                 ReferenceLinker referenceLinker) {
207             super(dataObject, metadata, dataObjectService, referenceLinker);
208         }
209     }
210 
211 }