001/** 002 * Copyright 2005-2018 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.service.impl; 017 018import java.lang.reflect.Field; 019import java.lang.reflect.InvocationTargetException; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.TreeMap; 029import java.util.UUID; 030import java.util.concurrent.ConcurrentHashMap; 031import java.util.concurrent.ConcurrentMap; 032import java.util.regex.Matcher; 033import java.util.regex.Pattern; 034 035import org.apache.commons.lang.StringUtils; 036import org.apache.commons.lang.reflect.FieldUtils; 037import org.eclipse.persistence.indirection.ValueHolder; 038import org.kuali.rice.core.api.config.property.ConfigurationService; 039import org.kuali.rice.core.api.criteria.QueryByCriteria; 040import org.kuali.rice.core.api.datetime.DateTimeService; 041import org.kuali.rice.core.api.exception.RiceRuntimeException; 042import org.kuali.rice.core.api.mo.common.GloballyUnique; 043import org.kuali.rice.core.api.mo.common.Versioned; 044import org.kuali.rice.core.api.search.SearchOperator; 045import org.kuali.rice.core.api.uif.RemotableQuickFinder; 046import org.kuali.rice.core.api.util.RiceKeyConstants; 047import org.kuali.rice.core.framework.persistence.ojb.conversion.OjbCharBooleanConversion; 048import org.kuali.rice.core.framework.persistence.platform.DatabasePlatform; 049import org.kuali.rice.krad.bo.BusinessObject; 050import org.kuali.rice.krad.bo.InactivatableFromTo; 051import org.kuali.rice.krad.bo.PersistableBusinessObject; 052import org.kuali.rice.krad.bo.PersistableBusinessObjectBaseAdapter; 053import org.kuali.rice.krad.bo.PersistableBusinessObjectExtension; 054import org.kuali.rice.krad.dao.DocumentDao; 055import org.kuali.rice.krad.dao.LookupDao; 056import org.kuali.rice.krad.dao.MaintenanceDocumentDao; 057import org.kuali.rice.krad.data.DataObjectService; 058import org.kuali.rice.krad.datadictionary.DataDictionaryEntry; 059import org.kuali.rice.krad.datadictionary.DataObjectEntry; 060import org.kuali.rice.krad.datadictionary.PrimitiveAttributeDefinition; 061import org.kuali.rice.krad.datadictionary.RelationshipDefinition; 062import org.kuali.rice.krad.datadictionary.SupportAttributeDefinition; 063import org.kuali.rice.krad.document.Document; 064import org.kuali.rice.krad.exception.ValidationException; 065import org.kuali.rice.krad.lookup.LookupUtils; 066import org.kuali.rice.krad.service.BusinessObjectService; 067import org.kuali.rice.krad.service.DataDictionaryService; 068import org.kuali.rice.krad.service.DataObjectMetaDataService; 069import org.kuali.rice.krad.service.KRADServiceLocator; 070import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 071import org.kuali.rice.krad.service.KualiModuleService; 072import org.kuali.rice.krad.service.LegacyDataAdapter; 073import org.kuali.rice.krad.service.ModuleService; 074import org.kuali.rice.krad.service.PersistenceService; 075import org.kuali.rice.krad.service.PersistenceStructureService; 076import org.kuali.rice.krad.uif.UifPropertyPaths; 077import org.kuali.rice.krad.uif.service.ViewDictionaryService; 078import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 079import org.kuali.rice.krad.util.ForeignKeyFieldsPopulationState; 080import org.kuali.rice.krad.util.GlobalVariables; 081import org.kuali.rice.krad.util.KRADConstants; 082import org.kuali.rice.krad.util.KRADPropertyConstants; 083import org.kuali.rice.krad.util.KRADUtils; 084import org.kuali.rice.krad.util.LegacyUtils; 085import org.kuali.rice.krad.util.ObjectUtils; 086import org.springframework.beans.PropertyAccessorUtils; 087import org.springframework.beans.factory.annotation.Required; 088 089/** 090* 091 */ 092@Deprecated 093public class KNSLegacyDataAdapterImpl implements LegacyDataAdapter { 094 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger( 095 KNSLegacyDataAdapterImpl.class); 096 097 private static final Pattern VALUE_HOLDER_FIELD_PATTERN = Pattern.compile("^_persistence_(.*)_vh$"); 098 099 private final ConcurrentMap<Class<?>, List<ValueHolderFieldPair>> valueHolderFieldCache = 100 new ConcurrentHashMap<Class<?>, List<ValueHolderFieldPair>>(8, 0.9f, 1); 101 102 private BusinessObjectService businessObjectService; 103 private PersistenceService persistenceService; 104 private LookupDao lookupDao; 105 private LookupCriteriaGenerator lookupCriteriaGenerator; 106 private DateTimeService dateTimeService; 107 private DatabasePlatform databasePlatform; 108 109 private DocumentDao documentDao; 110 private MaintenanceDocumentDao maintenanceDocumentDaoOjb; 111 112 private PersistenceStructureService persistenceStructureService; 113 private DataObjectMetaDataService dataObjectMetaDataService; 114 private ConfigurationService kualiConfigurationService; 115 private KualiModuleService kualiModuleService; 116 private DataDictionaryService dataDictionaryService; 117 private DataObjectService dataObjectService; 118 private ViewDictionaryService viewDictionaryService; 119 120 @Override 121 public <T> T save(T dataObject) { 122 if (dataObject instanceof Collection) { 123 Collection<Object> newList = new ArrayList<Object>(((Collection) dataObject).size()); 124 for (Object obj : (Collection<?>) dataObject) { 125 newList.add(save(obj)); 126 } 127 return (T) newList; 128 } else { 129 return (T) businessObjectService.save((PersistableBusinessObject) dataObject); 130 } 131 } 132 133 @Override 134 public <T> T linkAndSave(T dataObject) { 135 return (T) businessObjectService.linkAndSave((PersistableBusinessObject) dataObject); 136 } 137 138 @Override 139 public <T> T saveDocument(T document) { 140 return (T) documentDao.save((Document) document); 141 } 142 143 @Override 144 public <T> T findByPrimaryKey(Class<T> clazz, Map<String, ?> primaryKeys) { 145 return (T) businessObjectService.findByPrimaryKey((Class<BusinessObject>) clazz, primaryKeys); 146 } 147 148 @Override 149 public <T> T findBySinglePrimaryKey(Class<T> clazz, Object primaryKey) { 150 return (T) businessObjectService.findBySinglePrimaryKey((Class<BusinessObject>) clazz, primaryKey); 151 } 152 153 @Override 154 public void delete(Object dataObject) { 155 if (dataObject instanceof Collection) { 156 for (Object dobj : (Collection) dataObject) { 157 delete(dobj); 158 } 159 } else { 160 businessObjectService.delete(dataObject); 161 } 162 } 163 164 @Override 165 public void deleteMatching(Class<?> type, Map<String, ?> fieldValues) { 166 businessObjectService.deleteMatching(type, fieldValues); 167 } 168 169 @Override 170 public <T> T retrieve(T dataObject) { 171 return (T) businessObjectService.retrieve(dataObject); 172 } 173 174 @Override 175 public <T> Collection<T> findAll(Class<T> clazz) { 176 // just find all objects of given type without any attribute criteria 177 return findMatching(clazz, Collections.<String, Object>emptyMap()); 178 } 179 180 @Override 181 public <T> Collection<T> findMatching(Class<T> clazz, Map<String, ?> fieldValues) { 182 return (Collection<T>) businessObjectService.findMatching((Class<BusinessObject>) clazz, fieldValues); 183 } 184 185 @Override 186 public <T> Collection<T> findMatchingOrderBy(Class<T> clazz, Map<String, ?> fieldValues, String sortField, 187 boolean sortAscending) { 188 return (Collection<T>) businessObjectService.findMatchingOrderBy((Class<BusinessObject>) clazz, fieldValues, 189 sortField, sortAscending); 190 } 191 192 @Override 193 public Map<String, ?> getPrimaryKeyFieldValues(Object dataObject) { 194 return persistenceService.getPrimaryKeyFieldValues(dataObject); 195 } 196 197 @Override 198 public void retrieveNonKeyFields(Object persistableObject) { 199 persistenceService.retrieveNonKeyFields(persistableObject); 200 synchronizeEclipseLinkWeavings(persistableObject); 201 } 202 203 @Override 204 public void retrieveReferenceObject(Object persistableObject, String referenceObjectName) { 205 persistenceService.retrieveReferenceObject(persistableObject, referenceObjectName); 206 synchronizeEclipseLinkWeavings(persistableObject, referenceObjectName); 207 } 208 209 /** 210 * @see ValueHolderFieldPair for information on why we need to do field sychronization related to weaving 211 */ 212 protected void synchronizeEclipseLinkWeavings(Object persistableObject, String propertyName) { 213 if (LegacyUtils.isKradDataManaged(persistableObject.getClass())) { 214 List<ValueHolderFieldPair> fieldPairs = loadValueHolderFieldPairs(persistableObject.getClass()); 215 for (ValueHolderFieldPair fieldPair : fieldPairs) { 216 if (fieldPair.field.getName().equals(propertyName)) { 217 fieldPair.synchronizeValueHolder(persistableObject); 218 } 219 } 220 } 221 } 222 223 /** 224 * @see ValueHolderFieldPair for information on why we need to do field sychronization related to weaving 225 */ 226 protected void synchronizeEclipseLinkWeavings(Object persistableObject) { 227 if (LegacyUtils.isKradDataManaged(persistableObject.getClass())) { 228 List<ValueHolderFieldPair> fieldPairs = loadValueHolderFieldPairs(persistableObject.getClass()); 229 for (ValueHolderFieldPair fieldPair : fieldPairs) { 230 fieldPair.synchronizeValueHolder(persistableObject); 231 } 232 } 233 } 234 235 /** 236 * @see ValueHolderFieldPair for information on why we need to do field sychronization related to weaving 237 */ 238 private List<ValueHolderFieldPair> loadValueHolderFieldPairs(Class<?> type) { 239 if (valueHolderFieldCache.get(type) == null) { 240 List<ValueHolderFieldPair> pairs = new ArrayList<ValueHolderFieldPair>(); 241 searchValueHolderFieldPairs(type, pairs); 242 valueHolderFieldCache.putIfAbsent(type, pairs); 243 } 244 return valueHolderFieldCache.get(type); 245 } 246 247 /** 248 * @see ValueHolderFieldPair for information on why we need to do field sychronization related to weaving 249 */ 250 private void searchValueHolderFieldPairs(Class<?> type, List<ValueHolderFieldPair> pairs) { 251 if (type.equals(Object.class)) { 252 return; 253 } 254 for (Field valueHolderField : type.getDeclaredFields()) { 255 Matcher matcher = VALUE_HOLDER_FIELD_PATTERN.matcher(valueHolderField.getName()); 256 if (matcher.matches()) { 257 valueHolderField.setAccessible(true); 258 String fieldName = matcher.group(1); 259 Field valueField = FieldUtils.getDeclaredField(type, fieldName, true); 260 if (valueField != null) { 261 pairs.add(new ValueHolderFieldPair(valueField, valueHolderField)); 262 } 263 } 264 } 265 searchValueHolderFieldPairs(type.getSuperclass(), pairs); 266 } 267 268 @Override 269 public void refreshAllNonUpdatingReferences(Object persistableObject) { 270 persistenceService.refreshAllNonUpdatingReferences((PersistableBusinessObject) persistableObject); 271 synchronizeEclipseLinkWeavings(persistableObject); 272 } 273 274 @Override 275 public boolean isProxied(Object object) { 276 if (object == null || (!(object instanceof BusinessObject) && !(object instanceof PersistableBusinessObjectBaseAdapter))) { 277 return false; 278 } 279 return persistenceService.isProxied(object); 280 } 281 282 @Override 283 public Object resolveProxy(Object o) { 284 return persistenceService.resolveProxy(o); 285 } 286 287 // Lookup methods 288 289 @Override 290 public <T> Collection<T> findCollectionBySearchHelper(Class<T> dataObjectClass, Map<String, String> formProperties, 291 boolean unbounded, boolean allPrimaryKeyValuesPresentAndNotWildcard, Integer searchResultsLimit) { 292 return lookupDao.findCollectionBySearchHelper(dataObjectClass, formProperties, unbounded, 293 allPrimaryKeyValuesPresentAndNotWildcard, searchResultsLimit); 294 } 295 296 //TODO: implement. currently is same implementation as above 297 @Override 298 public <T> Collection<T> findCollectionBySearchHelper(Class<T> dataObjectClass, Map<String, String> formProperties, 299 List<String> wildcardAsLiteralPropertyNames, boolean unbounded, 300 boolean allPrimaryKeyValuesPresentAndNotWildcard, Integer searchResultsLimit) { 301 return lookupDao.findCollectionBySearchHelper(dataObjectClass, formProperties, unbounded, 302 allPrimaryKeyValuesPresentAndNotWildcard, searchResultsLimit); 303 } 304 305 /** 306 * 307 * @param dataObjectClass the dataobject class 308 * @param formProperties the incoming lookup form properties 309 * @param unbounded whether the search is unbounded 310 * @param searchResultsLimit the searchResultsLimit; null implies use of default KNS value if set for the class 311 * @param <T> the data object type 312 * @return collection of lookup results 313 */ 314 protected <T> Collection<T> performDataObjectServiceLookup(Class<T> dataObjectClass, 315 Map<String, String> formProperties, boolean unbounded, boolean allPrimaryKeyValuesPresentAndNotWildcard, 316 Integer searchResultsLimit) { 317 if (!unbounded && searchResultsLimit == null) { 318 // use KRAD LookupUtils.getSearchResultsLimit instead of KNS version. we have no LookupForm, so pass null, only the class will be used 319 //searchResultsLimit = LookupUtils.getSearchResultsLimit(example, null); 320 searchResultsLimit = org.kuali.rice.kns.lookup.LookupUtils.getSearchResultsLimit(dataObjectClass); 321 } 322 QueryByCriteria.Builder query = lookupCriteriaGenerator.generateCriteria(dataObjectClass, formProperties, 323 allPrimaryKeyValuesPresentAndNotWildcard); 324 if (!unbounded && searchResultsLimit != null) { 325 query.setMaxResults(searchResultsLimit); 326 } 327 328 Collection<T> results = dataObjectService.findMatching(dataObjectClass, query.build()).getResults(); 329 return filterCurrentDataObjects(dataObjectClass, results, formProperties); 330 } 331 332 protected <T> Collection<T> filterCurrentDataObjects(Class<T> dataObjectClass, Collection<T> unfiltered, 333 Map<String, String> formProps) { 334 if (InactivatableFromTo.class.isAssignableFrom(dataObjectClass)) { 335 Boolean currentSpecifier = lookupCriteriaCurrentSpecifier(formProps); 336 if (currentSpecifier != null) { 337 List<InactivatableFromTo> onlyCurrent = 338 KRADServiceLocator.getInactivateableFromToService().filterOutNonCurrent(new ArrayList( 339 unfiltered), new Date(LookupUtils.getActiveDateTimestampForCriteria(formProps) 340 .getTime())); 341 if (currentSpecifier) { 342 return (Collection<T>) onlyCurrent; 343 } else { 344 unfiltered.removeAll(onlyCurrent); 345 return unfiltered; 346 } 347 } 348 } 349 return unfiltered; 350 } 351 352 protected Boolean lookupCriteriaCurrentSpecifier(Map<String, String> formProps) { 353 String value = formProps.get(KRADPropertyConstants.CURRENT); 354 if (StringUtils.isNotBlank(value)) { 355 // FIXME: use something more portable than this direct OJB converter 356 String currentBooleanStr = (String) new OjbCharBooleanConversion().javaToSql(value); 357 if (OjbCharBooleanConversion.DATABASE_BOOLEAN_TRUE_STRING_REPRESENTATION.equals(currentBooleanStr)) { 358 return Boolean.TRUE; 359 } else if (OjbCharBooleanConversion.DATABASE_BOOLEAN_FALSE_STRING_REPRESENTATION.equals( 360 currentBooleanStr)) { 361 return Boolean.FALSE; 362 } 363 } 364 return null; 365 } 366 367 @Override 368 public <T> T findObjectBySearch(Class<T> type, Map<String, String> formProps) { 369 return lookupDao.findObjectByMap(type, formProps); 370 } 371 372 /** 373 * Returns whether all primary key values are specified in the lookup map and do not contain any wildcards 374 * 375 * @param boClass the bo class to lookup 376 * @param formProps the incoming form/lookup properties 377 * @return whether all primary key values are specified in the lookup map and do not contain any wildcards 378 */ 379 @Override 380 public boolean allPrimaryKeyValuesPresentAndNotWildcard(Class<?> boClass, Map<String, String> formProps) { 381 List<String> pkFields = listPrimaryKeyFieldNames(boClass); 382 Iterator<String> pkIter = pkFields.iterator(); 383 boolean returnVal = true; 384 while (returnVal && pkIter.hasNext()) { 385 String pkName = pkIter.next(); 386 String pkValue = formProps.get(pkName); 387 388 if (StringUtils.isBlank(pkValue)) { 389 returnVal = false; 390 } else { 391 for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) { 392 if (pkValue.contains(op.op())) { 393 returnVal = false; 394 break; 395 } 396 } 397 } 398 } 399 return returnVal; 400 } 401 402 @SuppressWarnings("unchecked") 403 @Override 404 public List<String> listPrimaryKeyFieldNames(Class<?> type) { 405 List<String> keys = new ArrayList<String>(); 406 if ( type == null ) { 407 return keys; 408 } 409 if (isPersistable(type)) { 410 keys = persistenceStructureService.listPrimaryKeyFieldNames(type); 411 } else { 412 ModuleService responsibleModuleService = kualiModuleService.getResponsibleModuleService(type); 413 if (responsibleModuleService != null && responsibleModuleService.isExternalizable(type)) { 414 keys = responsibleModuleService.listPrimaryKeyFieldNames(type); 415 } else { 416 // check the Data Dictionary for PK's of non PBO/EBO 417 DataObjectEntry dataObjectEntry = dataDictionaryService.getDataDictionary().getDataObjectEntry(type.getName()); 418 if ( dataObjectEntry != null ) { 419 List<String> pks = dataObjectEntry.getPrimaryKeys(); 420 if (pks != null ) { 421 keys = pks; 422 } 423 } else { 424 LOG.warn( "Unable to retrieve data object entry for non-persistable KNS-managed class: " + type.getName() ); 425 } 426 } 427 } 428 return keys; 429 } 430 431 /** 432 * LookupServiceImpl calls BusinessObjectMetaDataService to listPrimaryKeyFieldNames. 433 * The BusinessObjectMetaDataService goes beyond the PersistenceStructureService to consult 434 * the associated ModuleService in determining the primary key field names. 435 * TODO: Do we need both listPrimaryKeyFieldNames/persistenceStructureService and 436 * listPrimaryKeyFieldNamesConsultingAllServices/businesObjectMetaDataService or 437 * can the latter superset be used for the former? 438 * 439 * @param type the data object class 440 * @return list of primary key field names, consulting persistence structure service, module service and 441 * datadictionary 442 */ 443 protected List<String> listPrimaryKeyFieldNamesConsultingAllServices(Class<?> type) { 444 return dataObjectMetaDataService.listPrimaryKeyFieldNames(type); 445 } 446 447 @Override 448 public Class<?> determineCollectionObjectType(Class<?> containingType, String collectionPropertyName) { 449 final Class<?> collectionObjectType; 450 if (isPersistable(containingType)) { 451 Map<String, Class> collectionClasses = new HashMap<String, Class>(); 452 collectionClasses = persistenceStructureService.listCollectionObjectTypes(containingType); 453 collectionObjectType = collectionClasses.get(collectionPropertyName); 454 } else { 455 throw new RuntimeException( 456 "Can't determine the Class of Collection elements because persistenceStructureService.isPersistable(" 457 + containingType.getName() 458 + ") returns false."); 459 } 460 return collectionObjectType; 461 462 } 463 464 @Override 465 public boolean hasReference(Class<?> boClass, String referenceName) { 466 return persistenceStructureService.hasReference(boClass, referenceName); 467 } 468 469 @Override 470 public boolean hasCollection(Class<?> boClass, String collectionName) { 471 return persistenceStructureService.hasCollection(boClass, collectionName); 472 } 473 474 @Override 475 public boolean isExtensionAttribute(Class<?> boClass, String attributePropertyName, Class<?> propertyType) { 476 return propertyType.equals(PersistableBusinessObjectExtension.class); 477 } 478 479 @Override 480 public Class<?> getExtensionAttributeClass(Class<?> boClass, String attributePropertyName) { 481 return persistenceStructureService.getBusinessObjectAttributeClass((Class<? extends PersistableBusinessObject>)boClass, attributePropertyName); 482 } 483 484 485 @Override 486 public Map<String, ?> getPrimaryKeyFieldValuesDOMDS(Object dataObject) { 487 return dataObjectMetaDataService.getPrimaryKeyFieldValues(dataObject); 488 } 489 490 @Override 491 public boolean equalsByPrimaryKeys(Object do1, Object do2) { 492 return dataObjectMetaDataService.equalsByPrimaryKeys(do1, do2); 493 } 494 495 @Override 496 public void materializeAllSubObjects(Object object) { 497 ObjectUtils.materializeAllSubObjects((PersistableBusinessObject) object); 498 } 499 500 @Override 501 public Class<?> getPropertyType(Object object, String propertyName) { 502 return ObjectUtils.getPropertyType(object, propertyName, persistenceStructureService); 503 } 504 505 @Override 506 public Object getExtension( 507 Class<?> businessObjectClass) throws InstantiationException, IllegalAccessException { 508 Class<? extends PersistableBusinessObjectExtension> extensionClass = 509 persistenceStructureService.getBusinessObjectAttributeClass((Class<? extends PersistableBusinessObject>) businessObjectClass, "extension"); 510 if (extensionClass != null) { 511 return extensionClass.newInstance(); 512 } 513 return null; 514 } 515 516 @Override 517 public void refreshReferenceObject(Object businessObject, String referenceObjectName) { 518 if (StringUtils.isNotBlank(referenceObjectName) && !StringUtils.equals(referenceObjectName, "extension")) { 519 if (persistenceStructureService.hasReference(businessObject.getClass(), referenceObjectName) 520 || persistenceStructureService.hasCollection(businessObject.getClass(), referenceObjectName)) { 521 retrieveReferenceObject(businessObject, referenceObjectName); 522 } 523 } 524 } 525 526 @Override 527 public boolean isLockable(Object object) { 528 return isPersistable(object.getClass()); 529 } 530 531 @Override 532 public void verifyVersionNumber(Object dataObject) { 533 if (isPersistable(dataObject.getClass())) { 534 Object pbObject = businessObjectService.retrieve(dataObject); 535 if ( dataObject instanceof Versioned ) { 536 Long pbObjectVerNbr = KRADUtils.isNull(pbObject) ? null : ((Versioned) pbObject).getVersionNumber(); 537 Long newObjectVerNbr = ((Versioned) dataObject).getVersionNumber(); 538 if (pbObjectVerNbr != null && !(pbObjectVerNbr.equals(newObjectVerNbr))) { 539 GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, 540 RiceKeyConstants.ERROR_VERSION_MISMATCH); 541 throw new ValidationException( 542 "Version mismatch between the local business object and the database business object"); 543 } 544 } 545 } 546 } 547 548 @Override 549 public RemotableQuickFinder.Builder createQuickFinder(Class<?> containingClass, String attributeName) { 550 return createQuickFinderLegacy(containingClass, attributeName); 551 } 552 553 /** 554 * Legacy implementation of createQuickFinder. Uses the legacy DataObjectMetadataService. 555 */ 556 protected RemotableQuickFinder.Builder createQuickFinderLegacy(Class<?> containingClass, String attributeName) { 557 Object sampleComponent; 558 try { 559 sampleComponent = containingClass.newInstance(); 560 } catch (InstantiationException e) { 561 throw new RiceRuntimeException(e); 562 } catch (IllegalAccessException e) { 563 throw new RiceRuntimeException(e); 564 } 565 566 String lookupClassName = null; 567 Map<String, String> fieldConversions = new HashMap<String, String>(); 568 Map<String, String> lookupParameters = new HashMap<String, String>(); 569 570 org.kuali.rice.krad.bo.DataObjectRelationship relationship = 571 getDataObjectRelationship(sampleComponent, containingClass, attributeName, "", 572 true, true, false); 573 if (relationship != null) { 574 lookupClassName = relationship.getRelatedClass().getName(); 575 576 for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) { 577 String fromField = entry.getValue(); 578 String toField = entry.getKey(); 579 fieldConversions.put(fromField, toField); 580 } 581 582 for (Map.Entry<String, String> entry : relationship.getParentToChildReferences().entrySet()) { 583 String fromField = entry.getKey(); 584 String toField = entry.getValue(); 585 586 if (relationship.getUserVisibleIdentifierKey() == null || relationship.getUserVisibleIdentifierKey() 587 .equals(fromField)) { 588 lookupParameters.put(fromField, toField); 589 } 590 } 591 } else { 592 // check for title attribute and if match build lookup to component class using pk fields 593 String titleAttribute = dataObjectMetaDataService.getTitleAttribute(containingClass); 594 if (StringUtils.equals(titleAttribute, attributeName)) { 595 lookupClassName = containingClass.getName(); 596 597 List<String> pkAttributes = dataObjectMetaDataService.listPrimaryKeyFieldNames(containingClass); 598 for (String pkAttribute : pkAttributes) { 599 fieldConversions.put(pkAttribute, pkAttribute); 600 if (!StringUtils.equals(pkAttribute, attributeName)) { 601 lookupParameters.put(pkAttribute, pkAttribute); 602 } 603 } 604 } 605 } 606 607 if (StringUtils.isNotBlank(lookupClassName)) { 608 String baseUrl = kualiConfigurationService.getPropertyValueAsString(KRADConstants.KRAD_LOOKUP_URL_KEY); 609 RemotableQuickFinder.Builder builder = RemotableQuickFinder.Builder.create(baseUrl, lookupClassName); 610 builder.setLookupParameters(lookupParameters); 611 builder.setFieldConversions(fieldConversions); 612 613 return builder; 614 } 615 616 return null; 617 } 618 619 620 @Override 621 public boolean isReferenceUpdatable(Class<?> type, String referenceName) { 622 return persistenceStructureService.isReferenceUpdatable(type, referenceName); 623 } 624 625 @SuppressWarnings("rawtypes") 626 @Override 627 public Map<String, Class> listReferenceObjectFields(Class<?> type) { 628 return persistenceStructureService.listReferenceObjectFields(type); 629 } 630 631 @Override 632 public boolean isCollectionUpdatable(Class<?> type, String collectionName) { 633 return persistenceStructureService.isCollectionUpdatable(type, collectionName); 634 } 635 636 @Override 637 public Map<String, Class> listCollectionObjectTypes(Class<?> type) { 638 return persistenceStructureService.listCollectionObjectTypes(type); 639 } 640 641 @Override 642 public BusinessObject getReferenceIfExists(Object bo, String referenceName) { 643 if (!(bo instanceof BusinessObject)) { 644 throw new UnsupportedOperationException("getReferenceIfExists only supports BusinessObject in KNS"); 645 } 646 647 return businessObjectService.getReferenceIfExists((BusinessObject) bo, referenceName); 648 } 649 650 @Override 651 public boolean allForeignKeyValuesPopulatedForReference(Object bo, String referenceName) { 652 if (!(bo instanceof PersistableBusinessObject)) { 653 throw new UnsupportedOperationException( 654 "getReferenceIfExists only supports PersistableBusinessObject in KNS"); 655 } 656 657 return persistenceService.allForeignKeyValuesPopulatedForReference((PersistableBusinessObject) bo, 658 referenceName); 659 } 660 661 /** 662 * gets the relationship that the attribute represents on the class 663 * 664 * @param c - the class to which the attribute belongs 665 * @param attributeName - property name for the attribute 666 * @return a relationship definition for the attribute 667 */ 668 @Override 669 public RelationshipDefinition getDictionaryRelationship(Class<?> c, String attributeName) { 670 DataDictionaryEntry entryBase = 671 KRADServiceLocatorWeb.getDataDictionaryService().getDataDictionary().getDictionaryObjectEntry( 672 c.getName()); 673 if (entryBase == null) { 674 return null; 675 } 676 677 RelationshipDefinition relationship = null; 678 679 List<RelationshipDefinition> ddRelationships = entryBase.getRelationships(); 680 681 int minKeys = Integer.MAX_VALUE; 682 for (RelationshipDefinition def : ddRelationships) { 683 // favor key sizes of 1 first 684 if (def.getPrimitiveAttributes().size() == 1) { 685 for (PrimitiveAttributeDefinition primitive : def.getPrimitiveAttributes()) { 686 if (primitive.getSourceName().equals(attributeName) || def.getObjectAttributeName().equals( 687 attributeName)) { 688 relationship = def; 689 minKeys = 1; 690 break; 691 } 692 } 693 } else if (def.getPrimitiveAttributes().size() < minKeys) { 694 for (PrimitiveAttributeDefinition primitive : def.getPrimitiveAttributes()) { 695 if (primitive.getSourceName().equals(attributeName) || def.getObjectAttributeName().equals( 696 attributeName)) { 697 relationship = def; 698 minKeys = def.getPrimitiveAttributes().size(); 699 break; 700 } 701 } 702 } 703 } 704 705 // check the support attributes 706 if (relationship == null) { 707 for (RelationshipDefinition def : ddRelationships) { 708 if (def.hasIdentifier()) { 709 if (def.getIdentifier().getSourceName().equals(attributeName)) { 710 relationship = def; 711 } 712 } 713 } 714 } 715 716 return relationship; 717 } 718 719 /** 720 * @see org.kuali.rice.krad.service.LegacyDataAdapter 721 */ 722 @Override 723 public String getTitleAttribute(Class<?> dataObjectClass) { 724 String titleAttribute = null; 725 DataObjectEntry entry = getDataObjectEntry(dataObjectClass); 726 if (entry != null) { 727 titleAttribute = entry.getTitleAttribute(); 728 } 729 return titleAttribute; 730 } 731 732 /** 733 * @param dataObjectClass 734 * @return DataObjectEntry for the given dataObjectClass, or null if 735 * there is none 736 * @throws IllegalArgumentException if the given Class is null 737 */ 738 protected DataObjectEntry getDataObjectEntry(Class<?> dataObjectClass) { 739 if (dataObjectClass == null) { 740 throw new IllegalArgumentException("invalid (null) dataObjectClass"); 741 } 742 743 DataObjectEntry entry = dataDictionaryService.getDataDictionary().getDataObjectEntry(dataObjectClass.getName()); 744 745 return entry; 746 } 747 748 /** 749 * @see org.kuali.rice.krad.service.LegacyDataAdapter#areNotesSupported(Class) 750 */ 751 @Override 752 public boolean areNotesSupported(Class<?> dataObjectClass) { 753 boolean hasNotesSupport = false; 754 755 DataObjectEntry entry = getDataObjectEntry(dataObjectClass); 756 if (entry != null) { 757 hasNotesSupport = entry.isBoNotesEnabled(); 758 } 759 760 return hasNotesSupport; 761 } 762 763 /** 764 * Grabs primary key fields and sorts them if sort field names is true 765 * 766 * @param dataObject 767 * @param sortFieldNames 768 * @return Map of sorted primary key field values 769 */ 770 public Map<String, ?> getPrimaryKeyFieldValues(Object dataObject, boolean sortFieldNames) { 771 Map<String, Object> keyFieldValues = (Map<String, Object>) getPrimaryKeyFieldValues(dataObject); 772 if (sortFieldNames) { 773 Map<String, Object> sortedKeyFieldValues = new TreeMap<String, Object>(); 774 sortedKeyFieldValues.putAll(keyFieldValues); 775 return sortedKeyFieldValues; 776 } 777 return keyFieldValues; 778 } 779 780 /** 781 * @see org.kuali.rice.krad.service.DataObjectMetaDataService#getDataObjectIdentifierString 782 */ 783 @Override 784 public String getDataObjectIdentifierString(Object dataObject) { 785 String identifierString = ""; 786 787 if (dataObject == null) { 788 identifierString = "Null"; 789 return identifierString; 790 } 791 792 Class<?> dataObjectClass = dataObject.getClass(); 793 // if Legacy and a PersistableBusinessObject or if not Legacy and implement GlobalLyUnique use the object id field 794 if ((PersistableBusinessObject.class.isAssignableFrom( 795 dataObjectClass)) || (!LegacyUtils.useLegacyForObject(dataObject) && GloballyUnique.class 796 .isAssignableFrom(dataObjectClass))) { 797 String objectId = ObjectPropertyUtils.getPropertyValue(dataObject, UifPropertyPaths.OBJECT_ID); 798 if (StringUtils.isBlank(objectId)) { 799 objectId = UUID.randomUUID().toString(); 800 ObjectPropertyUtils.setPropertyValue(dataObject, UifPropertyPaths.OBJECT_ID, objectId); 801 } 802 } 803 return identifierString; 804 } 805 806 @Override 807 public Class<?> getInquiryObjectClassIfNotTitle(Object dataObject, String propertyName) { 808 Class<?> objectClass = ObjectUtils.materializeClassForProxiedObject(dataObject); 809 org.kuali.rice.krad.bo.DataObjectRelationship relationship = 810 dataObjectMetaDataService.getDataObjectRelationship(dataObject, objectClass, propertyName, "", true, 811 false, true); 812 if (relationship != null) { 813 return relationship.getRelatedClass(); 814 } 815 return null; 816 } 817 818 @Override 819 public Map<String, String> getInquiryParameters(Object dataObject, List<String> keys, String propertyName) { 820 Map<String, String> inquiryParameters = new HashMap<String, String>(); 821 Class<?> objectClass = ObjectUtils.materializeClassForProxiedObject(dataObject); 822 org.kuali.rice.krad.bo.DataObjectRelationship relationship = 823 dataObjectMetaDataService.getDataObjectRelationship(dataObject, objectClass, propertyName, "", true, 824 false, true); 825 for (String keyName : keys) { 826 String keyConversion = keyName; 827 if (relationship != null) { 828 keyConversion = relationship.getParentAttributeForChildAttribute(keyName); 829 } else if (PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName)) { 830 String nestedAttributePrefix = KRADUtils.getNestedAttributePrefix(propertyName); 831 keyConversion = nestedAttributePrefix + "." + keyName; 832 } 833 inquiryParameters.put(keyConversion, keyName); 834 } 835 return inquiryParameters; 836 } 837 838 @Override 839 public boolean hasLocalLookup(Class<?> dataObjectClass) { 840 return dataObjectMetaDataService.hasLocalLookup(dataObjectClass); 841 } 842 843 @Override 844 public boolean hasLocalInquiry(Class<?> dataObjectClass) { 845 return dataObjectMetaDataService.hasLocalInquiry(dataObjectClass); 846 } 847 848 @Override 849 public org.kuali.rice.krad.bo.DataObjectRelationship getDataObjectRelationship(Object dataObject, 850 Class<?> dataObjectClass, String attributeName, String attributePrefix, boolean keysOnly, 851 boolean supportsLookup, boolean supportsInquiry) { 852 RelationshipDefinition ddReference = getDictionaryRelationship(dataObjectClass, attributeName); 853 return dataObjectMetaDataService.getDataObjectRelationship(dataObject, dataObjectClass, attributeName, 854 attributePrefix, keysOnly, supportsLookup, supportsInquiry); 855 856 } 857 858 859 860 protected org.kuali.rice.krad.bo.DataObjectRelationship populateRelationshipFromDictionaryReference(Class<?> dataObjectClass, 861 RelationshipDefinition ddReference, String attributePrefix, boolean keysOnly) { 862 org.kuali.rice.krad.bo.DataObjectRelationship relationship = new org.kuali.rice.krad.bo.DataObjectRelationship(dataObjectClass, 863 ddReference.getObjectAttributeName(), ddReference.getTargetClass()); 864 865 for (PrimitiveAttributeDefinition def : ddReference.getPrimitiveAttributes()) { 866 if (StringUtils.isNotBlank(attributePrefix)) { 867 relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(), 868 def.getTargetName()); 869 } else { 870 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName()); 871 } 872 } 873 874 if (!keysOnly) { 875 for (SupportAttributeDefinition def : ddReference.getSupportAttributes()) { 876 if (StringUtils.isNotBlank(attributePrefix)) { 877 relationship.getParentToChildReferences().put(attributePrefix + "." + def.getSourceName(), 878 def.getTargetName()); 879 if (def.isIdentifier()) { 880 relationship.setUserVisibleIdentifierKey(attributePrefix + "." + def.getSourceName()); 881 } 882 } else { 883 relationship.getParentToChildReferences().put(def.getSourceName(), def.getTargetName()); 884 if (def.isIdentifier()) { 885 relationship.setUserVisibleIdentifierKey(def.getSourceName()); 886 } 887 } 888 } 889 } 890 891 return relationship; 892 } 893 894 @Override 895 public boolean isPersistable(Class<?> dataObjectClass) { 896 return persistenceStructureService.isPersistable(dataObjectClass); 897 } 898 899 @Override 900 public void setObjectPropertyDeep(Object bo, String propertyName, Class type, 901 Object propertyValue) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 902 ObjectUtils.setObjectPropertyDeep(bo,propertyName,type,propertyValue); 903 } 904 905 protected org.kuali.rice.krad.bo.DataObjectRelationship getRelationshipMetadata(Class<?> dataObjectClass, 906 String attributeName, String attributePrefix) { 907 908 RelationshipDefinition relationshipDefinition = getDictionaryRelationship(dataObjectClass, attributeName); 909 if (relationshipDefinition == null) { 910 return null; 911 } 912 913 org.kuali.rice.krad.bo.DataObjectRelationship dataObjectRelationship = 914 new org.kuali.rice.krad.bo.DataObjectRelationship(relationshipDefinition.getSourceClass(), 915 relationshipDefinition.getObjectAttributeName(), relationshipDefinition.getTargetClass()); 916 917 if (!StringUtils.isEmpty(attributePrefix)) { 918 attributePrefix += "."; 919 } 920 921 List<PrimitiveAttributeDefinition> primitives = relationshipDefinition.getPrimitiveAttributes(); 922 for (PrimitiveAttributeDefinition primitiveAttributeDefinition : primitives) { 923 dataObjectRelationship.getParentToChildReferences().put( 924 attributePrefix + primitiveAttributeDefinition.getSourceName(), 925 primitiveAttributeDefinition.getTargetName()); 926 } 927 928 return dataObjectRelationship; 929 } 930 931 protected boolean classHasSupportedFeatures(Class relationshipClass, boolean supportsLookup, 932 boolean supportsInquiry) { 933 boolean hasSupportedFeatures = true; 934 if (supportsLookup && !getViewDictionaryService().isLookupable(relationshipClass)) { 935 hasSupportedFeatures = false; 936 } 937 if (supportsInquiry && !getViewDictionaryService().isInquirable(relationshipClass)) { 938 hasSupportedFeatures = false; 939 } 940 941 return hasSupportedFeatures; 942 } 943 944 @Override 945 public boolean isNull(Object object){ 946 return ObjectUtils.isNull(object); 947 } 948 949 @Override 950 public void setObjectProperty(Object bo, String propertyName, Class propertyType, 951 Object propertyValue) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{ 952 ObjectUtils.setObjectProperty(bo,propertyName,propertyType,propertyValue); 953 } 954 955 @Override 956 public Class materializeClassForProxiedObject(Object object){ 957 return ObjectUtils.materializeClassForProxiedObject(object); 958 } 959 960 @Override 961 public Object getNestedValue(Object bo, String fieldName){ 962 return ObjectUtils.getNestedValue(bo,fieldName); 963 } 964 965 @Override 966 public Object createNewObjectFromClass(Class clazz){ 967 return ObjectUtils.createNewObjectFromClass(clazz); 968 } 969 970 @Override 971 public ForeignKeyFieldsPopulationState getForeignKeyFieldsPopulationState(Object dataObject, String referenceName) { 972 return persistenceStructureService.getForeignKeyFieldsPopulationState( 973 (PersistableBusinessObject) dataObject, referenceName); 974 } 975 976 @Override 977 public Map<String, String> getForeignKeysForReference(Class<?> clazz, String attributeName) { 978 return persistenceStructureService.getForeignKeysForReference(clazz, attributeName); 979 } 980 981 @Override 982 public boolean hasPrimaryKeyFieldValues(Object dataObject) { 983 return persistenceStructureService.hasPrimaryKeyFieldValues(dataObject); 984 } 985 986 @Override 987 public <T extends Document> T findByDocumentHeaderId(Class<T> documentClass, String id) { 988 return documentDao.findByDocumentHeaderId(documentClass, id); 989 } 990 991 @Override 992 public <T extends Document> List<T> findByDocumentHeaderIds(Class<T> documentClass, List<String> ids) { 993 return documentDao.findByDocumentHeaderIds(documentClass, ids); 994 } 995 996 997 @Required 998 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 999 this.businessObjectService = businessObjectService; 1000 } 1001 1002 @Required 1003 public void setPersistenceService(PersistenceService persistenceService) { 1004 this.persistenceService = persistenceService; 1005 } 1006 1007 @Required 1008 public void setLookupDao(LookupDao lookupDao) { 1009 this.lookupDao = lookupDao; 1010 } 1011 1012 @Required 1013 public void setLookupCriteriaGenerator(LookupCriteriaGenerator lookupCriteriaGenerator) { 1014 this.lookupCriteriaGenerator = lookupCriteriaGenerator; 1015 } 1016 1017 @Required 1018 public void setDateTimeService(DateTimeService dts) { 1019 this.dateTimeService = dts; 1020 } 1021 1022 @Required 1023 public void setDatabasePlatform(DatabasePlatform databasePlatform) { 1024 this.databasePlatform = databasePlatform; 1025 } 1026 1027 @Required 1028 public void setMaintenanceDocumentDaoOjb(MaintenanceDocumentDao maintenanceDocumentDaoOjb) { 1029 this.maintenanceDocumentDaoOjb = maintenanceDocumentDaoOjb; 1030 } 1031 1032 //@Required 1033 //public void setNoteDaoOjb(NoteDao noteDaoOjb) { 1034 // this.noteDaoOjb = noteDaoOjb; 1035 //} 1036 1037 @Required 1038 public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) { 1039 this.persistenceStructureService = persistenceStructureService; 1040 } 1041 1042 @Required 1043 public void setDataObjectMetaDataService(DataObjectMetaDataService dataObjectMetaDataService) { 1044 this.dataObjectMetaDataService = dataObjectMetaDataService; 1045 } 1046 1047 @Required 1048 public void setKualiConfigurationService(ConfigurationService kualiConfigurationService) { 1049 this.kualiConfigurationService = kualiConfigurationService; 1050 } 1051 1052 @Required 1053 public void setKualiModuleService(KualiModuleService kualiModuleService) { 1054 this.kualiModuleService = kualiModuleService; 1055 } 1056 1057 @Required 1058 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 1059 this.dataDictionaryService = dataDictionaryService; 1060 } 1061 1062 @Required 1063 public void setDocumentDao(DocumentDao documentDao) { 1064 this.documentDao = documentDao; 1065 } 1066 1067 public DataObjectService getDataObjectService() { 1068 return dataObjectService; 1069 } 1070 1071 public void setDataObjectService(DataObjectService dataObjectService) { 1072 this.dataObjectService = dataObjectService; 1073 } 1074 1075 public ViewDictionaryService getViewDictionaryService() { 1076 return viewDictionaryService; 1077 } 1078 1079 public void setViewDictionaryService(ViewDictionaryService viewDictionaryService) { 1080 this.viewDictionaryService = viewDictionaryService; 1081 } 1082 1083 /** 1084 * Holds a reference to a property Field on a JPA entity and the associated EclipseLink {@link org.eclipse.persistence.indirection.ValueHolder} field. 1085 * 1086 * <p>This exists to support the issue of using a data object with both OJB and JPA. EclipseLink will "instrument" 1087 * the entity class using load-time or static weaving (depending on what's been configured). For fields which are 1088 * FetchType.LAZY, EclipseLink will weave a private internal field with a name like "_persistence_propertyName_vh" 1089 * where "propertyName" is the name of the property which is lazy. This type of this field is {@link org.eclipse.persistence.indirection.ValueHolder}. 1090 * In this case, if you call getPropertyName for the property, the internal code will pull the value from the 1091 * ValueHolder instead of the actual property field. Oftentimes this will be ok because the two fields will be in 1092 * sync, but when using methods like OJB's retrieveReference and retrieveAllReferences, after the "retrieve" these 1093 * values will be out of sync. So the {@link #synchronizeValueHolder(Object)} method on this class can be used to 1094 * synchronize these values and ensure that the local field has a value which matches the ValueHolder.</p> 1095 */ 1096 private static final class ValueHolderFieldPair { 1097 1098 final Field field; 1099 final Field valueHolderField; 1100 1101 ValueHolderFieldPair(Field field, Field valueHolderField) { 1102 this.field = field; 1103 this.valueHolderField = valueHolderField; 1104 } 1105 1106 void synchronizeValueHolder(Object object) { 1107 try { 1108 ValueHolder valueHolder = (ValueHolder)valueHolderField.get(object); 1109 if(valueHolder != null){ 1110 Object value = field.get(object); 1111 valueHolder.setValue(value); 1112 } 1113 } catch (IllegalAccessException e) { 1114 throw new RuntimeException(e); 1115 } 1116 } 1117 1118 } 1119 1120}