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