Java tutorial
/** * 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(); } }