001package org.kuali.rice.krad.dao.impl;
002
003import org.apache.commons.lang.StringUtils;
004import org.apache.ojb.broker.query.Criteria;
005import org.apache.ojb.broker.query.QueryByCriteria;
006import org.apache.ojb.broker.query.QueryFactory;
007import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb;
008import org.kuali.rice.kns.service.KNSServiceLocator;
009import org.kuali.rice.krad.bo.BusinessObject;
010import org.kuali.rice.krad.bo.PersistableBusinessObject;
011import org.kuali.rice.krad.dao.BusinessObjectDao;
012import org.kuali.rice.krad.service.PersistenceStructureService;
013import org.kuali.rice.krad.service.util.OjbCollectionAware;
014import org.kuali.rice.krad.service.util.OjbCollectionHelper;
015import org.kuali.rice.krad.util.KRADPropertyConstants;
016import org.kuali.rice.krad.util.ObjectUtils;
017import org.springframework.dao.DataAccessException;
018import org.springframework.orm.ObjectRetrievalFailureException;
019
020import java.lang.reflect.InvocationTargetException;
021import java.util.*;
022
023/**
024 * Created by angelind on 11/6/14.
025 *
026 * Overridden by 'Sheik Salahudeen'
027 * Overridden for fixing the document fetching issue in KRAD Transaction document with OJB.
028 * Modified method names: findBySinglePrimaryKey,findByPrimaryKey,findAll,findAllOrderBy,findMatching,findAllActive,findAllInactive,findAllActiveOrderBy,findMatchingActive,findMatchingOrderBy,findByPrimaryKeyUsingKeyObject,buildCriteria
029 * Changes description : Modified the return type of method from '<T extends BusinessObject>' to  '<T>'.
030 */
031@Deprecated
032public class BusinessObjectDaoOjb extends PlatformAwareDaoBaseOjb implements BusinessObjectDao, OjbCollectionAware {
033    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BusinessObjectDaoOjb.class);
034
035    private PersistenceStructureService persistenceStructureService;
036    private OjbCollectionHelper ojbCollectionHelper;
037
038    /**
039     * This constructs a {@link BusinessObjectDaoOjb}
040     */
041    public BusinessObjectDaoOjb(PersistenceStructureService persistenceStructureService) {
042        this.persistenceStructureService = persistenceStructureService;
043    }
044
045    /**
046     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findBySinglePrimaryKey(java.lang.Class, java.lang.Object)
047     */
048    public <T> T findBySinglePrimaryKey(Class<T> clazz, Object primaryKey) {
049        if (primaryKey.getClass().getName().startsWith("java.lang.")
050                || primaryKey.getClass().getName().startsWith("java.sql.")
051                || primaryKey.getClass().getName().startsWith("java.math.")
052                || primaryKey.getClass().getName().startsWith("java.util.")) {
053            try {
054                return (T) getPersistenceBrokerTemplate().getObjectById(clazz, primaryKey);
055            } catch (  ObjectRetrievalFailureException ex  ) {
056                // it doesn't exist, just return null
057                return null;
058            }
059        } else {
060            Criteria criteria = buildCriteria(clazz, primaryKey);
061
062            return (T) getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQuery(clazz, criteria));
063        }
064    }
065
066    /**
067     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findByPrimaryKey(java.lang.Class, java.util.Map)
068     */
069    public <T> T findByPrimaryKey(Class<T> clazz, Map<String, ?> primaryKeys) {
070        Criteria criteria = buildCriteria(primaryKeys);
071
072        return (T) getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQuery(clazz, criteria));
073    }
074
075    /**
076     * Retrieves all of the records for a given class name.
077     *
078     * @param clazz - the name of the object being used, either KualiCodeBase or a subclass
079     * @return Collection
080     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAll(java.lang.Class)
081     */
082    public <T> Collection<T> findAll(Class<T> clazz) {
083        return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, (Criteria) null));
084    }
085
086    /**
087     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAllOrderBy(java.lang.Class, java.lang.String, boolean)
088     */
089    public <T> Collection<T> findAllOrderBy(Class<T> clazz, String sortField, boolean sortAscending) {
090        QueryByCriteria queryByCriteria = new QueryByCriteria(clazz, (Criteria) null);
091
092        if (sortAscending) {
093            queryByCriteria.addOrderByAscending(sortField);
094        }
095        else {
096            queryByCriteria.addOrderByDescending(sortField);
097        }
098
099        return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(queryByCriteria);
100    }
101
102    /**
103     * This is the default impl that comes with Kuali - uses OJB.
104     *
105     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findMatching(java.lang.Class, java.util.Map)
106     */
107    public <T> Collection<T> findMatching(Class<T> clazz, Map<String, ?> fieldValues) {
108        Criteria criteria = buildCriteria(fieldValues);
109
110        return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, criteria));
111    }
112
113    /**
114     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAllActive(java.lang.Class)
115     */
116    public <T> Collection<T> findAllActive(Class<T> clazz) {
117        return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, buildActiveCriteria()));
118    }
119
120    /**
121     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAllActive(java.lang.Class)
122     */
123    public <T> Collection<T> findAllInactive(Class<T> clazz) {
124        return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, buildInactiveCriteria()));
125    }
126
127    /**
128     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findAllActiveOrderBy(java.lang.Class, java.lang.String, boolean)
129     */
130    public <T> Collection<T> findAllActiveOrderBy(Class<T> clazz, String sortField, boolean sortAscending) {
131        QueryByCriteria queryByCriteria = new QueryByCriteria(clazz, buildActiveCriteria());
132
133        if (sortAscending) {
134            queryByCriteria.addOrderByAscending(sortField);
135        }
136        else {
137            queryByCriteria.addOrderByDescending(sortField);
138        }
139
140        return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(queryByCriteria);
141    }
142
143    /**
144     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findMatchingActive(java.lang.Class, java.util.Map)
145     */
146    public <T> Collection<T> findMatchingActive(Class<T> clazz, Map<String, ?> fieldValues) {
147        Criteria criteria = buildCriteria(fieldValues);
148        criteria.addAndCriteria(buildActiveCriteria());
149
150        return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(clazz, criteria));
151    }
152
153    /**
154     * This is the default impl that comes with Kuali - uses OJB.
155     *
156     * @see org.kuali.rice.krad.dao.BusinessObjectDao#countMatching(java.lang.Class, java.util.Map)
157     */
158    public int countMatching(Class clazz, Map<String, ?> fieldValues) {
159        Criteria criteria = buildCriteria(fieldValues);
160
161        return getPersistenceBrokerTemplate().getCount(QueryFactory.newQuery(clazz, criteria));
162    }
163
164    /**
165     * This is the default impl that comes with Kuali - uses OJB.
166     *
167     * @see org.kuali.rice.krad.dao.BusinessObjectDao#countMatching(java.lang.Class, java.util.Map, java.util.Map)
168     */
169    public int countMatching(Class clazz, Map<String, ?> positiveFieldValues, Map<String, ?> negativeFieldValues) {
170        Criteria criteria = buildCriteria(positiveFieldValues);
171        Criteria negativeCriteria = buildNegativeCriteria(negativeFieldValues);
172        criteria.addAndCriteria(negativeCriteria);
173        return getPersistenceBrokerTemplate().getCount(QueryFactory.newQuery(clazz, criteria));
174    }
175
176
177    /**
178     * This is the default impl that comes with Kuali - uses OJB.
179     *
180     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findMatching(java.lang.Class, java.util.Map)
181     */
182    public <T> Collection<T> findMatchingOrderBy(Class<T> clazz, Map<String, ?> fieldValues, String sortField, boolean sortAscending) {
183        Criteria criteria = buildCriteria(fieldValues);
184        QueryByCriteria queryByCriteria = new QueryByCriteria(clazz, criteria);
185
186        if (sortAscending) {
187            queryByCriteria.addOrderByAscending(sortField);
188        }
189        else {
190            queryByCriteria.addOrderByDescending(sortField);
191        }
192
193        return (Collection<T>)getPersistenceBrokerTemplate().getCollectionByQuery(queryByCriteria);
194    }
195
196    /**
197     * Saves a business object.
198     *
199     * @see org.kuali.rice.krad.dao.BusinessObjectDao#save(org.kuali.rice.krad.bo.PersistableBusinessObject)
200     */
201    public PersistableBusinessObject save(PersistableBusinessObject bo) throws DataAccessException {
202        // if collections exist on the BO, create a copy and use to process the
203        // collections to ensure
204        // that removed elements are deleted from the database
205        Set<String> boCollections = getPersistenceStructureService().listCollectionObjectTypes(bo.getClass()).keySet();
206        PersistableBusinessObject savedBo = null;
207        if (!boCollections.isEmpty()) {
208            // refresh bo to get db copy of collections
209            savedBo = (PersistableBusinessObject) ObjectUtils.deepCopy(bo);
210            for (String boCollection : boCollections) {
211                if (getPersistenceStructureService().isCollectionUpdatable(savedBo.getClass(), boCollection)) {
212                    savedBo.refreshReferenceObject(boCollection);
213                }
214            }
215            getOjbCollectionHelper().processCollections(this, bo, savedBo);
216        }
217
218        getPersistenceBrokerTemplate().store(bo);
219        return bo;
220    }
221
222    /**
223     * Saves a business object.
224     *
225     * @see org.kuali.rice.krad.dao.BusinessObjectDao#save(org.kuali.rice.krad.bo.PersistableBusinessObject)
226     */
227    public List<? extends PersistableBusinessObject> save(List businessObjects) throws DataAccessException {
228        if ( LOG.isDebugEnabled() ) {
229            LOG.debug( "About to persist the following BOs:" );
230            for ( Object bo : businessObjects ) {
231                LOG.debug( "   --->" + bo );
232            }
233        }
234        for (Iterator i = businessObjects.iterator(); i.hasNext();) {
235            Object bo = i.next();
236            getPersistenceBrokerTemplate().store(bo);
237        }
238        return businessObjects;
239    }
240
241
242    /**
243     * Deletes the business object passed in.
244     *
245     * @param bo
246     * @throws DataAccessException
247     * @see org.kuali.rice.krad.dao.BusinessObjectDao#delete(org.kuali.rice.krad.bo.PersistableBusinessObject)
248     */
249    public void delete(Object bo) {
250        getPersistenceBrokerTemplate().delete(bo);
251    }
252
253    /**
254     * @see org.kuali.rice.krad.dao.BusinessObjectDao#delete(java.util.List)
255     */
256    public void delete(List<? extends PersistableBusinessObject> boList) {
257        for (PersistableBusinessObject bo : boList) {
258            getPersistenceBrokerTemplate().delete(bo);
259        }
260    }
261
262
263    /**
264     * @see org.kuali.rice.krad.dao.BusinessObjectDao#deleteMatching(java.lang.Class, java.util.Map)
265     */
266    public void deleteMatching(Class clazz, Map<String, ?> fieldValues) {
267        Criteria criteria = buildCriteria(fieldValues);
268
269        getPersistenceBrokerTemplate().deleteByQuery(QueryFactory.newQuery(clazz, criteria));
270
271        // An ojb delete by query doesn't update the cache so we need to clear the cache for everything to work property.
272        // don't believe me? Read the source code to OJB
273        getPersistenceBrokerTemplate().clearCache();
274    }
275
276    /**
277     * @see org.kuali.rice.krad.dao.BusinessObjectDao#retrieve(org.kuali.rice.krad.bo.PersistableBusinessObject)
278     */
279    public Object retrieve(Object object) {
280        return getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQueryByIdentity(object));
281    }
282
283    /**
284     * OJB does not support this method
285     * @see org.kuali.rice.krad.dao.BusinessObjectDao#findByPrimaryKey(java.lang.Class, java.lang.Object)
286     */
287    public  <T> T findByPrimaryKeyUsingKeyObject(Class<T> clazz, Object pkObject) {
288        throw new UnsupportedOperationException("OJB does not support this option");
289    }
290
291    /**
292     * No need to do anything - avoid saving and OJB will "manage read only"
293     * @see org.kuali.rice.krad.dao.BusinessObjectDao#manageReadOnly(org.kuali.rice.krad.bo.PersistableBusinessObject)
294     */
295    public PersistableBusinessObject manageReadOnly(PersistableBusinessObject bo) {
296        return bo;
297    }
298
299    /**
300     * This method will build out criteria in the key-value paradigm (attribute-value).
301     *
302     * @param fieldValues
303     * @return
304     */
305    private Criteria buildCriteria(Map<String, ?> fieldValues) {
306        Criteria criteria = new Criteria();
307        for (Iterator i = fieldValues.entrySet().iterator(); i.hasNext();) {
308            Map.Entry<String, Object> e = (Map.Entry<String, Object>) i.next();
309
310            String key = e.getKey();
311            Object value = e.getValue();
312            if (value instanceof Collection) {
313                criteria.addIn(key, (Collection) value);
314            } else {
315                criteria.addEqualTo(key, value);
316            }
317        }
318
319        return criteria;
320    }
321
322
323    private <T> Criteria buildCriteria(Class<T> clazz, Object primaryKey) {
324        Map<String, Object> fieldValues = new HashMap<String, Object>();
325        List<String> fieldNames = getPersistenceStructureService().getPrimaryKeys(clazz);
326
327        //create map of values
328        for (String fieldName : fieldNames) {
329            Object fieldValue;
330
331            try {
332                fieldValue = primaryKey.getClass().getMethod("get" + StringUtils.capitalize(fieldName)).invoke(primaryKey);
333                fieldValues.put(fieldName, fieldValue);
334            } catch (IllegalArgumentException e) {
335                e.printStackTrace();
336            } catch (IllegalAccessException e) {
337                e.printStackTrace();
338            } catch (SecurityException e) {
339                e.printStackTrace();
340            } catch (InvocationTargetException e) {
341                e.printStackTrace();
342            } catch (NoSuchMethodException e) {
343                e.printStackTrace();
344            }
345        }
346        return this.buildCriteria(fieldValues);
347    }
348
349    /**
350     * Builds a Criteria object for active field set to true
351     * @return Criteria
352     */
353    private Criteria buildActiveCriteria(){
354        Criteria criteria = new Criteria();
355        criteria.addEqualTo(KRADPropertyConstants.ACTIVE, true);
356
357        return criteria;
358    }
359
360    /**
361     * Builds a Criteria object for active field set to true
362     * @return Criteria
363     */
364    private Criteria buildInactiveCriteria(){
365        Criteria criteria = new Criteria();
366        criteria.addEqualTo(KRADPropertyConstants.ACTIVE, false);
367
368        return criteria;
369    }
370
371    /**
372     * This method will build out criteria in the key-value paradigm (attribute-value).
373     *
374     * @param negativeFieldValues
375     * @return
376     */
377    private Criteria buildNegativeCriteria(Map<String, ?> negativeFieldValues) {
378        Criteria criteria = new Criteria();
379        for (Iterator i = negativeFieldValues.entrySet().iterator(); i.hasNext();) {
380            Map.Entry<String, Object> e = (Map.Entry<String, Object>) i.next();
381
382            String key = e.getKey();
383            Object value = e.getValue();
384            if (value instanceof Collection) {
385                criteria.addNotIn(key, (Collection) value);
386            }
387            else {
388                criteria.addNotEqualTo(key, value);
389            }
390        }
391
392        return criteria;
393    }
394
395    /**
396     * Gets the persistenceStructureService attribute.
397     * @return Returns the persistenceStructureService.
398     */
399    protected PersistenceStructureService getPersistenceStructureService() {
400        return persistenceStructureService;
401    }
402
403    /**
404     * Sets the persistenceStructureService attribute value.
405     * @param persistenceStructureService The persistenceStructureService to set.
406     */
407    public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
408        this.persistenceStructureService = persistenceStructureService;
409    }
410
411    /**
412     * OJB collection helper instance which processes collections before persisting
413     *
414     * @return OjbCollectionHelper
415     */
416    protected OjbCollectionHelper getOjbCollectionHelper() {
417        if (ojbCollectionHelper == null) {
418            ojbCollectionHelper = KNSServiceLocator.getOjbCollectionHelper();
419        }
420
421        return ojbCollectionHelper;
422    }
423
424    /**
425     * Setter for the OJB collection helper
426     *
427     * @param ojbCollectionHelper
428     */
429    public void setOjbCollectionHelper(OjbCollectionHelper ojbCollectionHelper) {
430        this.ojbCollectionHelper = ojbCollectionHelper;
431    }
432}