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