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