org.kuali.kfs.sys.batch.dataaccess.impl.FiscalYearMakersDaoOjb.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kfs.sys.batch.dataaccess.impl.FiscalYearMakersDaoOjb.java

Source

/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 * 
 * Copyright 2005-2014 The Kuali Foundation
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.sys.batch.dataaccess.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.ojb.broker.query.QueryByCriteria;
import org.apache.ojb.broker.query.ReportQueryByCriteria;
import org.apache.ojb.broker.util.ObjectModification;
import org.kuali.kfs.sys.batch.dataaccess.FiscalYearMaker;
import org.kuali.kfs.sys.batch.dataaccess.FiscalYearMakersDao;
import org.kuali.kfs.sys.businessobject.FiscalYearBasedBusinessObject;
import org.kuali.rice.core.framework.persistence.ojb.dao.PlatformAwareDaoBaseOjb;
import org.kuali.rice.krad.bo.PersistableBusinessObject;
import org.kuali.rice.krad.util.ObjectUtils;

/**
 * @see org.kuali.kfs.coa.batch.dataaccess.FiscalYearMakersDao
 */
public class FiscalYearMakersDaoOjb extends PlatformAwareDaoBaseOjb implements FiscalYearMakersDao {
    private static final Logger LOG = org.apache.log4j.Logger.getLogger(FiscalYearMakersDaoOjb.class);

    protected static final String KEY_STRING_DELIMITER = "|";

    /**
     * @see org.kuali.kfs.coa.batch.dataaccess.FiscalYearMakersDao#deleteNewYearRows(java.lang.Integer,
     *      org.kuali.kfs.coa.batch.dataaccess.FiscalYearMakerHelper)
     */
    public void deleteNewYearRows(Integer baseYear, FiscalYearMaker objectFiscalYearMaker) {
        if (LOG.isInfoEnabled()) {
            LOG.info(String.format("\ndeleting %s for target year(s)",
                    objectFiscalYearMaker.getBusinessObjectClass().getName()));
        }

        QueryByCriteria queryID = new QueryByCriteria(objectFiscalYearMaker.getBusinessObjectClass(),
                objectFiscalYearMaker.createDeleteCriteria(baseYear));
        getPersistenceBrokerTemplate().deleteByQuery(queryID);

        getPersistenceBrokerTemplate().clearCache();
    }

    /**
     * @see org.kuali.kfs.sys.batch.dataaccess.FiscalYearMakersDao#createNewYearRows(java.lang.Integer,
     *      org.kuali.kfs.sys.batch.dataaccess.FiscalYearMaker, boolean, java.util.Map)
     */
    public Collection<String> createNewYearRows(Integer baseYear, FiscalYearMaker fiscalYearMaker,
            boolean replaceMode, Map<Class<? extends FiscalYearBasedBusinessObject>, Set<String>> parentKeysWritten,
            boolean isParentClass) throws Exception {
        if (LOG.isInfoEnabled()) {
            LOG.info(String.format("\n copying %s from %d to %d",
                    fiscalYearMaker.getBusinessObjectClass().getName(), baseYear, baseYear + 1));
        }

        int rowsRead = 0;
        int rowsWritten = 0;
        int rowsFailingRI = 0;

        // list of copy error messages to be written out at end
        List<String> copyErrors = new ArrayList<String>();

        // Set of primary key strings written
        Set<String> keysWritten = new HashSet<String>();

        // retrieve the list of next-year PKs for the given object
        List<String> primaryKeyFields = fiscalYearMaker.getPrimaryKeyPropertyNames();

        Set<String> nextYearPrimaryKeys = new HashSet<String>(2000);
        LOG.info("Loading Next Year's PKs for comparison");
        ReportQueryByCriteria nextYearKeyQuery = new ReportQueryByCriteria(fiscalYearMaker.getBusinessObjectClass(),
                primaryKeyFields.toArray(new String[0]), fiscalYearMaker.createNextYearSelectionCriteria(baseYear));
        Iterator<Object[]> nextYearRecords = getPersistenceBrokerTemplate()
                .getReportQueryIteratorByQuery(nextYearKeyQuery);
        StringBuilder keyString = new StringBuilder(40);
        int numNextYearRecords = 0;
        while (nextYearRecords.hasNext()) {
            numNextYearRecords++;
            keyString.setLength(0);
            Object[] record = nextYearRecords.next();
            for (Object f : record) {
                keyString.append(f).append(KEY_STRING_DELIMITER);
            }
            nextYearPrimaryKeys.add(keyString.toString());
            if (numNextYearRecords % 10000 == 0) {
                if (LOG.isInfoEnabled()) {
                    LOG.info("Processing Record: " + numNextYearRecords);
                }
            }
        }
        if (LOG.isInfoEnabled()) {
            LOG.info("Completed load of next year keys.  " + numNextYearRecords + " keys loaded.");
            LOG.info("Starting processing of existing FY rows");
        }
        // retrieve base year records to copy
        QueryByCriteria queryId = new QueryByCriteria(fiscalYearMaker.getBusinessObjectClass(),
                fiscalYearMaker.createSelectionCriteria(baseYear));
        // BIG QUERY - GET ALL RECORDS for the current FY 
        Iterator<FiscalYearBasedBusinessObject> recordsToCopy = getPersistenceBrokerTemplate()
                .getIteratorByQuery(queryId);

        while (recordsToCopy.hasNext()) {
            FiscalYearBasedBusinessObject objectToCopy = recordsToCopy.next();
            rowsRead++;
            if (LOG.isInfoEnabled()) {
                if (rowsRead % 1000 == 0) {
                    LOG.info("*** Processing Record: " + rowsRead + " -- Written So Far: " + rowsWritten
                            + " -- Failing RI: " + rowsFailingRI + " -- Keys Written: " + keysWritten.size());
                }
            }

            // remove reference/collection fields so they will not cause an issue with the insert
            removeNonPrimitiveFields(fiscalYearMaker, objectToCopy);

            // set record fields for new year
            fiscalYearMaker.changeForNewYear(baseYear, objectToCopy);

            // determine if new year record already exists and if so do not overwrite
            if (nextYearPrimaryKeys.contains(getKeyString(fiscalYearMaker, primaryKeyFields, objectToCopy))) {
                if (isParentClass) {
                    addToKeysWritten(fiscalYearMaker, primaryKeyFields, objectToCopy, keysWritten);
                }
                continue;
            }

            // check parent records exist so RI will be satisfied
            if (!validateParentRecordsExist(fiscalYearMaker, objectToCopy, parentKeysWritten, copyErrors)) {
                rowsFailingRI++;
                continue;
            }

            // store new record
            getPersistenceBroker(true).store(objectToCopy, ObjectModification.INSERT);
            rowsWritten++;
            if (isParentClass) {
                addToKeysWritten(fiscalYearMaker, primaryKeyFields, objectToCopy, keysWritten);
            }
        }

        if (isParentClass) {
            parentKeysWritten.put(fiscalYearMaker.getBusinessObjectClass(), keysWritten);
        }

        if (LOG.isInfoEnabled()) {
            LOG.info(String.format("\n%s:\n%d read = %d\n%d written = %d\nfailed RI = %d",
                    fiscalYearMaker.getBusinessObjectClass(), baseYear, rowsRead, baseYear + 1, rowsWritten,
                    rowsFailingRI));
        }

        getPersistenceBrokerTemplate().clearCache();

        return copyErrors;
    }

    /**
     * Sets all reference and collection fields defined in the persistence layer to null on the given object
     * 
     * @param businessObject object to set properties for
     */
    protected void removeNonPrimitiveFields(FiscalYearMaker fiscalYearMaker,
            FiscalYearBasedBusinessObject businessObject) {
        try {
            @SuppressWarnings("rawtypes")
            Map<String, Class> referenceFields = fiscalYearMaker.getReferenceObjectProperties();
            for (String fieldName : referenceFields.keySet()) {
                if (!fieldName.equals("extension")) {
                    PropertyUtils.setSimpleProperty(businessObject, fieldName, null);
                }
            }

            @SuppressWarnings("rawtypes")
            Map<String, Class> collectionFields = fiscalYearMaker.getCollectionProperties();
            for (String fieldName : collectionFields.keySet()) {
                PropertyUtils.setSimpleProperty(businessObject, fieldName, null);
            }
        } catch (Exception e) {
            throw new RuntimeException("Unable to set non primitive fields to null: " + e.getMessage(), e);
        }
    }

    /**
     * Checks all parents for the object we are copying has a corresponding record for the child record
     * 
     * @return true if all parent records exist, false otherwise
     */
    protected boolean validateParentRecordsExist(FiscalYearMaker objectFiscalYearMaker,
            FiscalYearBasedBusinessObject childRecord,
            Map<Class<? extends FiscalYearBasedBusinessObject>, Set<String>> parentKeysWritten,
            List<String> copyErrors) throws Exception {
        // iterate through all parents, get attribute reference name and attempt to retrieve
        for (Class<? extends FiscalYearBasedBusinessObject> parentClass : objectFiscalYearMaker
                .getParentClasses()) {
            if (!validateChildParentReferencesExist(objectFiscalYearMaker, childRecord, parentClass,
                    parentKeysWritten.get(parentClass), copyErrors)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Validates the parent record(s) exists for the child record by retrieving the OJB reference (if found and foreign keys have
     * value)
     * 
     * @param childRecord child record we are inserting
     * @param parentClass class for parent of child
     * @param parentKeys Set of parent key Strings that have been written
     * @param copyErrors Collection for adding error messages
     * @return true if the parent record(s) exist, false otherwise
     */
    protected boolean validateChildParentReferencesExist(FiscalYearMaker objectFiscalYearMaker,
            FiscalYearBasedBusinessObject childRecord, Class<? extends FiscalYearBasedBusinessObject> parentClass,
            Set<String> parentKeys, List<String> copyErrors) throws Exception {
        boolean allChildParentReferencesExist = true;
        boolean foundParentReference = false;

        // get all references for child class
        @SuppressWarnings("rawtypes")
        Map<String, Class> referenceObjects = objectFiscalYearMaker.getReferenceObjectProperties();

        // iterate through to find references with the parent class
        for (String referenceName : referenceObjects.keySet()) {
            Class<? extends PersistableBusinessObject> referenceClass = referenceObjects.get(referenceName);

            if (parentClass.isAssignableFrom(referenceClass)) {
                foundParentReference = true;

                String foreignKeyString = getForeignKeyStringForReference(objectFiscalYearMaker, childRecord,
                        referenceName);
                if (StringUtils.isNotBlank(foreignKeyString) && !parentKeys.contains(foreignKeyString)) {
                    // attempt to retrieve the parent reference in case it already existed
                    getPersistenceBroker(true).retrieveReference(childRecord, referenceName);
                    PersistableBusinessObject reference = (PersistableBusinessObject) PropertyUtils
                            .getSimpleProperty(childRecord, referenceName);
                    if (ObjectUtils.isNull(reference)) {
                        allChildParentReferencesExist = false;
                        writeMissingParentCopyError(childRecord, parentClass, foreignKeyString, copyErrors);
                        LOG.warn("Missing Parent Object: " + copyErrors.get(copyErrors.size() - 1));
                    } else {
                        parentKeys.add(foreignKeyString);
                    }
                }
            }
        }

        if (!foundParentReference) {
            LOG.warn(
                    String.format("\n!!! NO relationships between child %s and parent %s found in OJB descriptor\n",
                            childRecord.getClass().getName(), parentClass.getName()));
        }

        return allChildParentReferencesExist;
    }

    /**
     * Builds a String containing foreign key values for the given reference of the business object
     * 
     * @param businessObject business object instance with reference
     * @param referenceName name of reference
     * @return String of foreign key values or null if any of the foreign key values are null
     */
    protected String getForeignKeyStringForReference(FiscalYearMaker fiscalYearMaker,
            FiscalYearBasedBusinessObject businessObject, String referenceName) throws Exception {
        Map<String, String> foreignKeyToPrimaryKeyMap = fiscalYearMaker.getForeignKeyMappings(referenceName);

        StringBuilder foreignKeyString = new StringBuilder(80);
        for (String fkFieldName : foreignKeyToPrimaryKeyMap.keySet()) {
            Object fkFieldValue = PropertyUtils.getSimpleProperty(businessObject, fkFieldName);
            if (fkFieldValue != null) {
                foreignKeyString.append(fkFieldValue.toString()).append(KEY_STRING_DELIMITER);
            } else {
                foreignKeyString.setLength(0);
                break;
            }
        }

        return foreignKeyString.toString();
    }

    /**
     * Builds an error message when a parent record was not found for the child
     * 
     * @param childRecord child record we are inserting
     * @param parentClass class for parent of child
     * @param foreignKeyString string of foreign key values that was not found in parent
     * @param copyErrors Collection for adding error messages
     */
    protected void writeMissingParentCopyError(FiscalYearBasedBusinessObject childRecord,
            Class<? extends FiscalYearBasedBusinessObject> parentClass, String foreignKeyString,
            Collection<String> copyErrors) {
        StringBuilder errorCopyFailedMessage = new StringBuilder(150);
        errorCopyFailedMessage.append(childRecord.getClass().getName());
        errorCopyFailedMessage.append(" row for " + childRecord.toString());
        errorCopyFailedMessage.append(" - " + foreignKeyString);
        errorCopyFailedMessage.append(" not in ");
        errorCopyFailedMessage.append(parentClass.getName());

        copyErrors.add(errorCopyFailedMessage.toString());
    }

    /**
     * Builds a string from the primary key values and adds to given set
     * 
     * @param copiedObject object to grab key values for
     * @param keysWritten Set containing all pk strings
     */
    protected void addToKeysWritten(FiscalYearMaker fiscalYearMaker, List<String> keyFieldNames,
            FiscalYearBasedBusinessObject copiedObject, Set<String> keysWritten) throws Exception {
        keysWritten.add(getKeyString(fiscalYearMaker, keyFieldNames, copiedObject));
    }

    protected String getKeyString(FiscalYearMaker fiscalYearMaker, List<String> keyFieldNames,
            FiscalYearBasedBusinessObject businessObject) throws Exception {
        StringBuilder keyString = new StringBuilder(40);
        for (String keyFieldName : keyFieldNames) {
            keyString.append(PropertyUtils.getSimpleProperty(businessObject, keyFieldName))
                    .append(KEY_STRING_DELIMITER);
        }
        return keyString.toString();
    }
}