org.kuali.rice.kns.lookup.KualiLookupableHelperServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.rice.kns.lookup.KualiLookupableHelperServiceImpl.java

Source

/**
 * Copyright 2005-2014 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.rice.kns.lookup;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.kuali.rice.core.api.CoreApiServiceLocator;
import org.kuali.rice.core.api.encryption.EncryptionService;
import org.kuali.rice.core.api.search.SearchOperator;
import org.kuali.rice.krad.bo.BusinessObject;
import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
import org.kuali.rice.krad.datadictionary.BusinessObjectEntry;
import org.kuali.rice.krad.datadictionary.RelationshipDefinition;
import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
import org.kuali.rice.krad.service.ModuleService;
import org.kuali.rice.krad.util.BeanPropertyComparator;
import org.kuali.rice.krad.util.ExternalizableBusinessObjectUtils;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.ObjectUtils;
import org.springframework.transaction.annotation.Transactional;

import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @deprecated Use {@link org.kuali.rice.krad.lookup.LookupableImpl}.
 */
@Deprecated
@Transactional
public class KualiLookupableHelperServiceImpl extends AbstractLookupableHelperServiceImpl {

    protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
            .getLogger(KualiLookupableHelperServiceImpl.class);
    protected boolean searchUsingOnlyPrimaryKeyValues = false;

    /**
     * Uses Lookup Service to provide a basic search.
     *
     * @param fieldValues - Map containing prop name keys and search values
     *
     * @return List found business objects
     * @see LookupableHelperService#getSearchResults(java.util.Map)
     */
    public List<? extends BusinessObject> getSearchResults(Map<String, String> fieldValues) {
        return getSearchResultsHelper(
                org.kuali.rice.krad.lookup.LookupUtils.forceUppercase(getBusinessObjectClass(), fieldValues),
                false);
    }

    /**
     * Uses Lookup Service to provide a basic unbounded search.
     *
     * @param fieldValues - Map containing prop name keys and search values
     *
     * @return List found business objects
     * @see LookupableHelperService#getSearchResultsUnbounded(java.util.Map)
     */
    public List<? extends BusinessObject> getSearchResultsUnbounded(Map<String, String> fieldValues) {
        return getSearchResultsHelper(
                org.kuali.rice.krad.lookup.LookupUtils.forceUppercase(getBusinessObjectClass(), fieldValues), true);
    }

    // TODO: Fix? - this does not handle nested properties within the EBO.

    /**
     * Check whether the given property represents a property within an EBO starting
     * with the sampleBo object given.  This is used to determine if a criteria needs
     * to be applied to the EBO first, before sending to the normal lookup DAO.
     */
    protected boolean isExternalBusinessObjectProperty(Object sampleBo, String propertyName) {
        try {
            if (propertyName.indexOf(".") > 0 && !StringUtils.contains(propertyName, "add.")) {
                Class propertyClass = PropertyUtils.getPropertyType(sampleBo,
                        StringUtils.substringBeforeLast(propertyName, "."));
                if (propertyClass != null) {
                    return ExternalizableBusinessObjectUtils.isExternalizableBusinessObjectInterface(propertyClass);
                } else {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("unable to get class for " + StringUtils.substringBeforeLast(propertyName, ".")
                                + " on " + sampleBo.getClass().getName());
                    }
                }
            }
        } catch (Exception e) {
            LOG.debug("Unable to determine type of property for " + sampleBo.getClass().getName() + "/"
                    + propertyName, e);
        }
        return false;
    }

    /**
     * Get the name of the property which represents the ExternalizableBusinessObject for the given property.
     *
     * This method can not handle nested properties within the EBO.
     *
     * Returns null if the property is not a nested property or is part of an add line.
     */
    protected String getExternalBusinessObjectProperty(Object sampleBo, String propertyName) {
        if (propertyName.indexOf(".") > 0 && !StringUtils.contains(propertyName, "add.")) {
            return StringUtils.substringBeforeLast(propertyName, ".");
        }
        return null;
    }

    /**
     * Checks whether any of the fieldValues being passed refer to a property within an ExternalizableBusinessObject.
     */
    protected boolean hasExternalBusinessObjectProperty(Class boClass, Map<String, String> fieldValues) {
        try {
            Object sampleBo = boClass.newInstance();
            for (String key : fieldValues.keySet()) {
                if (isExternalBusinessObjectProperty(sampleBo, key)) {
                    return true;
                }
            }
        } catch (Exception ex) {
            LOG.debug("Unable to check " + boClass + " for EBO properties.", ex);
        }
        return false;
    }

    /**
     * Returns a map stripped of any properties which refer to ExternalizableBusinessObjects.  These values may not be passed into the
     * lookup service, since the objects they refer to are not in the local database.
     */
    protected Map<String, String> removeExternalizableBusinessObjectFieldValues(Class boClass,
            Map<String, String> fieldValues) {
        Map<String, String> eboFieldValues = new HashMap<String, String>();
        try {
            Object sampleBo = boClass.newInstance();
            for (String key : fieldValues.keySet()) {
                if (!isExternalBusinessObjectProperty(sampleBo, key)) {
                    eboFieldValues.put(key, fieldValues.get(key));
                }
            }
        } catch (Exception ex) {
            LOG.debug("Unable to check " + boClass + " for EBO properties.", ex);
        }
        return eboFieldValues;
    }

    /**
     * Return the EBO fieldValue entries explicitly for the given eboPropertyName.  (I.e., any properties with the given
     * property name as a prefix.
     */
    protected Map<String, String> getExternalizableBusinessObjectFieldValues(String eboPropertyName,
            Map<String, String> fieldValues) {
        Map<String, String> eboFieldValues = new HashMap<String, String>();
        for (String key : fieldValues.keySet()) {
            if (key.startsWith(eboPropertyName + ".")) {
                eboFieldValues.put(StringUtils.substringAfterLast(key, "."), fieldValues.get(key));
            }
        }
        return eboFieldValues;
    }

    /**
     * Get the complete list of all properties referenced in the fieldValues that are ExternalizableBusinessObjects.
     *
     * This is a list of the EBO object references themselves, not of the properties within them.
     */
    protected List<String> getExternalizableBusinessObjectProperties(Class boClass,
            Map<String, String> fieldValues) {
        Set<String> eboPropertyNames = new HashSet<String>();
        try {
            Object sampleBo = boClass.newInstance();
            for (String key : fieldValues.keySet()) {
                if (isExternalBusinessObjectProperty(sampleBo, key)) {
                    eboPropertyNames.add(StringUtils.substringBeforeLast(key, "."));
                }
            }
        } catch (Exception ex) {
            LOG.debug("Unable to check " + boClass + " for EBO properties.", ex);
        }
        return new ArrayList<String>(eboPropertyNames);
    }

    /**
     * Given an property on the main BO class, return the defined type of the ExternalizableBusinessObject.  This will be used
     * by other code to determine the correct module service to call for the lookup.
     *
     * @param boClass
     * @param propertyName
     * @return
     */
    protected Class<? extends ExternalizableBusinessObject> getExternalizableBusinessObjectClass(Class boClass,
            String propertyName) {
        try {
            return PropertyUtils.getPropertyType(boClass.newInstance(),
                    StringUtils.substringBeforeLast(propertyName, "."));
        } catch (Exception e) {
            LOG.debug("Unable to determine type of property for " + boClass.getName() + "/" + propertyName, e);
        }
        return null;
    }

    /**
     *
     * This method does the actual search, with the parameters specified, and returns the result.
     *
     * NOTE that it will not do any upper-casing based on the DD forceUppercase. That is handled through an external call to
     * LookupUtils.forceUppercase().
     *
     * @param fieldValues A Map of the fieldNames and fieldValues to be searched on.
     * @param unbounded Whether the results should be bounded or not to a certain max size.
     * @return A List of search results.
     *
     */
    protected List<? extends BusinessObject> getSearchResultsHelper(Map<String, String> fieldValues,
            boolean unbounded) {
        // remove hidden fields
        LookupUtils.removeHiddenCriteriaFields(getBusinessObjectClass(), fieldValues);

        searchUsingOnlyPrimaryKeyValues = getLookupService()
                .allPrimaryKeyValuesPresentAndNotWildcard(getBusinessObjectClass(), fieldValues);

        setBackLocation(fieldValues.get(KRADConstants.BACK_LOCATION));
        setDocFormKey(fieldValues.get(KRADConstants.DOC_FORM_KEY));
        setReferencesToRefresh(fieldValues.get(KRADConstants.REFERENCES_TO_REFRESH));
        List searchResults;
        Map<String, String> nonBlankFieldValues = new HashMap<String, String>();
        for (String fieldName : fieldValues.keySet()) {
            String fieldValue = fieldValues.get(fieldName);
            if (StringUtils.isNotBlank(fieldValue)) {
                if (fieldValue.endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) {
                    String encryptedValue = StringUtils.removeEnd(fieldValue,
                            EncryptionService.ENCRYPTION_POST_PREFIX);
                    try {
                        if (CoreApiServiceLocator.getEncryptionService().isEnabled()) {
                            fieldValue = getEncryptionService().decrypt(encryptedValue);
                        }
                    } catch (GeneralSecurityException e) {
                        LOG.error("Error decrypting value for business object " + getBusinessObjectService()
                                + " attribute " + fieldName, e);
                        throw new RuntimeException("Error decrypting value for business object "
                                + getBusinessObjectService() + " attribute " + fieldName, e);
                    }
                }
                nonBlankFieldValues.put(fieldName, fieldValue);
            }
        }

        // If this class is an EBO, just call the module service to get the results
        if (ExternalizableBusinessObjectUtils.isExternalizableBusinessObject(getBusinessObjectClass())) {
            ModuleService eboModuleService = KRADServiceLocatorWeb.getKualiModuleService()
                    .getResponsibleModuleService(getBusinessObjectClass());
            BusinessObjectEntry ddEntry = eboModuleService
                    .getExternalizableBusinessObjectDictionaryEntry(getBusinessObjectClass());
            Map<String, String> filteredFieldValues = new HashMap<String, String>();
            for (String fieldName : nonBlankFieldValues.keySet()) {
                if (ddEntry.getAttributeNames().contains(fieldName)) {
                    filteredFieldValues.put(fieldName, nonBlankFieldValues.get(fieldName));
                }
            }
            searchResults = eboModuleService.getExternalizableBusinessObjectsListForLookup(getBusinessObjectClass(),
                    (Map) filteredFieldValues, unbounded);
            // if any of the properties refer to an embedded EBO, call the EBO lookups first and apply to the local lookup
        } else if (hasExternalBusinessObjectProperty(getBusinessObjectClass(), nonBlankFieldValues)) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("has EBO reference: " + getBusinessObjectClass());
                LOG.debug("properties: " + nonBlankFieldValues);
            }
            // remove the EBO criteria
            Map<String, String> nonEboFieldValues = removeExternalizableBusinessObjectFieldValues(
                    getBusinessObjectClass(), nonBlankFieldValues);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Non EBO properties removed: " + nonEboFieldValues);
            }
            // get the list of EBO properties attached to this object
            List<String> eboPropertyNames = getExternalizableBusinessObjectProperties(getBusinessObjectClass(),
                    nonBlankFieldValues);
            if (LOG.isDebugEnabled()) {
                LOG.debug("EBO properties: " + eboPropertyNames);
            }
            // loop over those properties
            for (String eboPropertyName : eboPropertyNames) {
                // extract the properties as known to the EBO
                Map<String, String> eboFieldValues = getExternalizableBusinessObjectFieldValues(eboPropertyName,
                        nonBlankFieldValues);
                if (LOG.isDebugEnabled()) {
                    LOG.debug("EBO properties for master EBO property: " + eboPropertyName);
                    LOG.debug("properties: " + eboFieldValues);
                }
                // run search against attached EBO's module service
                ModuleService eboModuleService = KRADServiceLocatorWeb.getKualiModuleService()
                        .getResponsibleModuleService(
                                getExternalizableBusinessObjectClass(getBusinessObjectClass(), eboPropertyName));
                // KULRICE-4401 made eboResults an empty list and only filled if service is found.
                List eboResults = Collections.emptyList();
                if (eboModuleService != null) {
                    eboResults = eboModuleService.getExternalizableBusinessObjectsListForLookup(
                            getExternalizableBusinessObjectClass(getBusinessObjectClass(), eboPropertyName),
                            (Map) eboFieldValues, unbounded);
                } else {
                    LOG.debug("EBO ModuleService is null: " + eboPropertyName);
                }
                // get the mapping/relationship between the EBO object and it's parent object
                // use that to adjust the fieldValues

                // get the parent property type
                Class eboParentClass;
                String eboParentPropertyName;
                if (ObjectUtils.isNestedAttribute(eboPropertyName)) {
                    eboParentPropertyName = StringUtils.substringBeforeLast(eboPropertyName, ".");
                    try {
                        eboParentClass = PropertyUtils.getPropertyType(getBusinessObjectClass().newInstance(),
                                eboParentPropertyName);
                    } catch (Exception ex) {
                        throw new RuntimeException("Unable to create an instance of the business object class: "
                                + getBusinessObjectClass().getName(), ex);
                    }
                } else {
                    eboParentClass = getBusinessObjectClass();
                    eboParentPropertyName = null;
                }
                if (LOG.isDebugEnabled()) {
                    LOG.debug("determined EBO parent class/property name: " + eboParentClass + "/"
                            + eboParentPropertyName);
                }
                // look that up in the DD (BOMDS)
                // find the appropriate relationship
                // CHECK THIS: what if eboPropertyName is a nested attribute - need to strip off the eboParentPropertyName if not null
                RelationshipDefinition rd = getBusinessObjectMetaDataService()
                        .getBusinessObjectRelationshipDefinition(eboParentClass, eboPropertyName);
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Obtained RelationshipDefinition for " + eboPropertyName);
                    LOG.debug(rd);
                }

                // copy the needed properties (primary only) to the field values
                // KULRICE-4446 do so only if the relationship definition exists
                // NOTE: this will work only for single-field PK unless the ORM layer is directly involved
                // (can't make (field1,field2) in ( (v1,v2),(v3,v4) ) style queries in the lookup framework
                if (ObjectUtils.isNotNull(rd)) {
                    if (rd.getPrimitiveAttributes().size() > 1) {
                        throw new RuntimeException(
                                "EBO Links don't work for relationships with multiple-field primary keys.");
                    }
                    String boProperty = rd.getPrimitiveAttributes().get(0).getSourceName();
                    String eboProperty = rd.getPrimitiveAttributes().get(0).getTargetName();
                    StringBuffer boPropertyValue = new StringBuffer();
                    // loop over the results, making a string that the lookup DAO will convert into an
                    // SQL "IN" clause
                    for (Object ebo : eboResults) {
                        if (boPropertyValue.length() != 0) {
                            boPropertyValue.append(SearchOperator.OR.op());
                        }
                        try {
                            boPropertyValue.append(PropertyUtils.getProperty(ebo, eboProperty).toString());
                        } catch (Exception ex) {
                            LOG.warn("Unable to get value for " + eboProperty + " on " + ebo);
                        }
                    }
                    if (eboParentPropertyName == null) {
                        // non-nested property containing the EBO
                        nonEboFieldValues.put(boProperty, boPropertyValue.toString());
                    } else {
                        // property nested within the main searched-for BO that contains the EBO
                        nonEboFieldValues.put(eboParentPropertyName + "." + boProperty, boPropertyValue.toString());
                    }
                }
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("Passing these results into the lookup service: " + nonEboFieldValues);
            }
            // add those results as criteria
            // run the normal search (but with the EBO critieria added)
            searchResults = (List) getLookupService().findCollectionBySearchHelper(getBusinessObjectClass(),
                    nonEboFieldValues, unbounded);
        } else {
            searchResults = (List) getLookupService().findCollectionBySearchHelper(getBusinessObjectClass(),
                    nonBlankFieldValues, unbounded);
        }

        if (searchResults == null) {
            searchResults = new ArrayList();
        }

        // sort list if default sort column given
        List defaultSortColumns = getDefaultSortColumns();
        if (defaultSortColumns.size() > 0) {
            Collections.sort(searchResults, new BeanPropertyComparator(defaultSortColumns, true));
        }
        return searchResults;
    }

    /**
     * @see LookupableHelperService#isSearchUsingOnlyPrimaryKeyValues()
     */
    @Override
    public boolean isSearchUsingOnlyPrimaryKeyValues() {
        return searchUsingOnlyPrimaryKeyValues;
    }

    /**
     * Returns a comma delimited list of primary key field labels, to be used on the UI to tell the user which fields were used to search
     *
     * These labels are generated from the DD definitions for the lookup fields
     *
     * @return a comma separated list of field attribute names.  If no fields found, returns "N/A"
     * @see LookupableHelperService#isSearchUsingOnlyPrimaryKeyValues()
     * @see LookupableHelperService#getPrimaryKeyFieldLabels()
     */
    @Override
    public String getPrimaryKeyFieldLabels() {
        StringBuilder buf = new StringBuilder();
        List<String> primaryKeyFieldNames = KRADServiceLocatorWeb.getLegacyDataAdapter()
                .listPrimaryKeyFieldNames(getBusinessObjectClass());
        Iterator<String> pkIter = primaryKeyFieldNames.iterator();
        while (pkIter.hasNext()) {
            String pkFieldName = (String) pkIter.next();
            buf.append(getDataDictionaryService().getAttributeLabel(getBusinessObjectClass(), pkFieldName));
            if (pkIter.hasNext()) {
                buf.append(", ");
            }
        }
        return buf.length() == 0 ? KRADConstants.NOT_AVAILABLE_STRING : buf.toString();
    }

}