1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.data.jpa.eclipselink;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.eclipse.persistence.descriptors.ClassDescriptor;
20 import org.eclipse.persistence.expressions.Expression;
21 import org.eclipse.persistence.internal.expressions.FunctionExpression;
22 import org.eclipse.persistence.internal.helper.DatabaseField;
23 import org.eclipse.persistence.internal.jpa.metamodel.EmbeddableTypeImpl;
24 import org.eclipse.persistence.internal.jpa.metamodel.EntityTypeImpl;
25 import org.eclipse.persistence.internal.jpa.metamodel.ManagedTypeImpl;
26 import org.eclipse.persistence.internal.jpa.metamodel.PluralAttributeImpl;
27 import org.eclipse.persistence.internal.jpa.metamodel.SingularAttributeImpl;
28 import org.eclipse.persistence.jpa.JpaEntityManager;
29 import org.eclipse.persistence.mappings.AggregateObjectMapping;
30 import org.eclipse.persistence.mappings.CollectionMapping;
31 import org.eclipse.persistence.mappings.DatabaseMapping;
32 import org.eclipse.persistence.mappings.DirectToFieldMapping;
33 import org.eclipse.persistence.mappings.ForeignReferenceMapping;
34 import org.eclipse.persistence.mappings.ManyToOneMapping;
35 import org.eclipse.persistence.mappings.OneToManyMapping;
36 import org.eclipse.persistence.mappings.OneToOneMapping;
37 import org.eclipse.persistence.mappings.converters.Converter;
38 import org.eclipse.persistence.mappings.converters.ConverterClass;
39 import org.eclipse.persistence.queries.ObjectLevelReadQuery;
40 import org.kuali.rice.krad.data.jpa.JpaMetadataProviderImpl;
41 import org.kuali.rice.krad.data.metadata.DataObjectAttributeRelationship;
42 import org.kuali.rice.krad.data.metadata.DataObjectCollectionSortAttribute;
43 import org.kuali.rice.krad.data.metadata.DataObjectMetadata;
44 import org.kuali.rice.krad.data.metadata.DataObjectRelationship;
45 import org.kuali.rice.krad.data.metadata.MetadataConfigurationException;
46 import org.kuali.rice.krad.data.metadata.SortDirection;
47 import org.kuali.rice.krad.data.metadata.impl.DataObjectAttributeImpl;
48 import org.kuali.rice.krad.data.metadata.impl.DataObjectAttributeRelationshipImpl;
49 import org.kuali.rice.krad.data.metadata.impl.DataObjectCollectionImpl;
50 import org.kuali.rice.krad.data.metadata.impl.DataObjectCollectionSortAttributeImpl;
51 import org.kuali.rice.krad.data.metadata.impl.DataObjectMetadataImpl;
52 import org.kuali.rice.krad.data.metadata.impl.DataObjectRelationshipImpl;
53 import org.kuali.rice.krad.data.metadata.impl.MetadataChildBase;
54
55 import javax.persistence.metamodel.Attribute.PersistentAttributeType;
56 import javax.persistence.metamodel.EntityType;
57 import javax.persistence.metamodel.ManagedType;
58 import javax.persistence.metamodel.PluralAttribute;
59 import javax.persistence.metamodel.SingularAttribute;
60 import java.lang.reflect.Field;
61 import java.util.ArrayList;
62 import java.util.Collection;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Set;
66
67
68
69
70 public class EclipseLinkJpaMetadataProviderImpl extends JpaMetadataProviderImpl {
71 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
72 .getLogger(EclipseLinkJpaMetadataProviderImpl.class);
73
74
75
76
77 @Override
78 protected void populateImplementationSpecificEntityLevelMetadata(DataObjectMetadataImpl metadata,
79 EntityType<?> entityType) {
80 if ( entityType instanceof EntityTypeImpl ) {
81 metadata.setBackingObjectName(((EntityTypeImpl<?>) entityType).getDescriptor().getTableName());
82 }
83 }
84
85
86
87
88 @Override
89 protected void populateImplementationSpecificAttributeLevelMetadata(DataObjectAttributeImpl attribute,
90 SingularAttribute<?, ?> attr) {
91
92 if (attr instanceof SingularAttributeImpl) {
93 DatabaseMapping mapping = ((SingularAttributeImpl<?, ?>) attr).getMapping();
94 if (mapping != null && mapping.getField() != null) {
95 attribute.setReadOnly(mapping.isReadOnly());
96 attribute.setBackingObjectName(mapping.getField().getName());
97 if (mapping.getField().getLength() != 0) {
98 attribute.setMaxLength((long) mapping.getField().getLength());
99 }
100
101
102
103
104 if (mapping instanceof DirectToFieldMapping) {
105 Converter converter = ((DirectToFieldMapping) mapping).getConverter();
106
107
108
109 if (converter != null && converter instanceof ConverterClass) {
110
111 try {
112 Field f = ConverterClass.class.getDeclaredField("attributeConverterClassName");
113 f.setAccessible(true);
114 String attributeConverterClassName = (String) f.get(converter);
115 if (StringUtils.containsIgnoreCase(attributeConverterClassName, "encrypt")) {
116 attribute.setSensitive(true);
117 }
118 } catch (Exception e) {
119 LOG.warn("Unable to access the converter name for attribute: "
120 + attribute.getOwningType().getName() + "." + attribute.getName()
121 + " Skipping attempt to detect converter.");
122 }
123 }
124 }
125
126 }
127 }
128 }
129
130
131
132
133 @Override
134 protected void populateImplementationSpecificCollectionLevelMetadata(DataObjectCollectionImpl collection,
135 PluralAttribute<?, ?, ?> cd) {
136
137 Class<?> collectionElementClass = cd.getElementType().getJavaType();
138 EntityType<?> elementEntityType = entityManager.getMetamodel().entity(collectionElementClass);
139
140 if (elementEntityType instanceof EntityTypeImpl) {
141 collection.setBackingObjectName(((EntityTypeImpl<?>) elementEntityType).getDescriptor().getTableName());
142 }
143
144
145 PersistentAttributeType persistentAttributeType = cd.getPersistentAttributeType();
146
147 if (cd instanceof PluralAttributeImpl) {
148 PluralAttributeImpl<?, ?, ?> coll = (PluralAttributeImpl<?, ?, ?>) cd;
149 CollectionMapping collectionMapping = coll.getCollectionMapping();
150
151 if (collectionMapping instanceof OneToManyMapping) {
152 OneToManyMapping otm = (OneToManyMapping) collectionMapping;
153 populateInverseRelationship(otm, collection);
154 Map<DatabaseField, DatabaseField> keyMap = otm.getSourceKeysToTargetForeignKeys();
155 List<DataObjectAttributeRelationship> attributeRelationships = new ArrayList<DataObjectAttributeRelationship>();
156 for (Map.Entry<DatabaseField, DatabaseField> keyRel : keyMap.entrySet()) {
157 attributeRelationships.add(new DataObjectAttributeRelationshipImpl(
158 getPropertyNameFromDatabaseColumnName(cd.getDeclaringType(), keyRel.getKey().getName()),
159 getPropertyNameFromDatabaseColumnName(elementEntityType, keyRel.getValue().getName())));
160 }
161 collection.setAttributeRelationships(attributeRelationships);
162 }
163
164 collection.setReadOnly(collectionMapping.isReadOnly());
165 collection.setSavedWithParent(collectionMapping.isCascadePersist());
166 collection.setDeletedWithParent(collectionMapping.isCascadeRemove());
167 collection.setLoadedAtParentLoadTime(collectionMapping.isCascadeRefresh() && !collectionMapping.isLazy());
168 collection.setLoadedDynamicallyUponUse(collectionMapping.isCascadeRefresh() && collectionMapping.isLazy());
169 } else {
170
171 collection.setReadOnly(false);
172 collection.setSavedWithParent(persistentAttributeType == PersistentAttributeType.ONE_TO_MANY);
173 collection.setDeletedWithParent(persistentAttributeType == PersistentAttributeType.ONE_TO_MANY);
174 collection.setLoadedAtParentLoadTime(true);
175 collection.setLoadedDynamicallyUponUse(false);
176 }
177
178
179
180
181
182
183
184 List<DataObjectCollectionSortAttribute> sortAttributes = new ArrayList<DataObjectCollectionSortAttribute>();
185 if (cd instanceof PluralAttributeImpl) {
186 PluralAttributeImpl<?, ?, ?> coll = (PluralAttributeImpl<?, ?, ?>) cd;
187 CollectionMapping collectionMapping = coll.getCollectionMapping();
188 if (collectionMapping.getSelectionQuery() instanceof ObjectLevelReadQuery) {
189 ObjectLevelReadQuery readQuery = (ObjectLevelReadQuery) collectionMapping.getSelectionQuery();
190 List<Expression> orderByExpressions = readQuery.getOrderByExpressions();
191 for (Expression expression : orderByExpressions) {
192 if (expression instanceof FunctionExpression) {
193 String attributeName = ((FunctionExpression) expression).getBaseExpression().getName();
194 SortDirection direction = SortDirection.ASCENDING;
195 if (expression.getOperator().isOrderOperator()) {
196 if (StringUtils
197 .containsIgnoreCase(expression.getOperator().getDatabaseStrings()[0], "DESC")) {
198 direction = SortDirection.DESCENDING;
199 }
200 }
201 sortAttributes.add(new DataObjectCollectionSortAttributeImpl(attributeName, direction));
202 }
203 }
204 }
205
206 }
207 collection.setDefaultCollectionOrderingAttributeNames(sortAttributes);
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225 @SuppressWarnings({ "unchecked", "rawtypes" })
226 protected String getPropertyNameFromDatabaseColumnName(ManagedType entityType, String databaseColumnName) {
227 for (SingularAttributeImpl attr : (Set<SingularAttributeImpl>) entityType.getSingularAttributes()) {
228 if (!attr.isAssociation()) {
229 if (!(attr.getClass().isAssignableFrom(EmbeddableTypeImpl.class)) &&
230 !(attr.getMapping().getClass().isAssignableFrom(AggregateObjectMapping.class)) &&
231 attr.getMapping().getField().getName().equals(databaseColumnName)) {
232 return attr.getName();
233 }
234 }
235 }
236 return null;
237 }
238
239
240
241
242 @Override
243 protected void populateImplementationSpecificRelationshipLevelMetadata(DataObjectRelationshipImpl relationship,
244 SingularAttribute<?, ?> rd) {
245
246 Class<?> referencedClass = rd.getBindableJavaType();
247 EntityType<?> referencedEntityType = entityManager.getMetamodel().entity(referencedClass);
248 if (referencedEntityType instanceof EntityTypeImpl) {
249 relationship
250 .setBackingObjectName(((EntityTypeImpl<?>) referencedEntityType).getDescriptor().getTableName());
251 }
252
253 PersistentAttributeType persistentAttributeType = rd.getPersistentAttributeType();
254
255 if (rd instanceof SingularAttributeImpl) {
256 SingularAttributeImpl<?, ?> rel = (SingularAttributeImpl<?, ?>) rd;
257
258 OneToOneMapping relationshipMapping = (OneToOneMapping) rel.getMapping();
259 relationship.setReadOnly(relationshipMapping.isReadOnly());
260 relationship.setSavedWithParent(relationshipMapping.isCascadePersist());
261 relationship.setDeletedWithParent(relationshipMapping.isCascadeRemove());
262 relationship.setLoadedAtParentLoadTime(relationshipMapping.isCascadeRefresh()
263 && !relationshipMapping.isLazy());
264 relationship.setLoadedDynamicallyUponUse(relationshipMapping.isCascadeRefresh()
265 && relationshipMapping.isLazy());
266
267 List<DataObjectAttributeRelationship> attributeRelationships = new ArrayList<DataObjectAttributeRelationship>();
268 for (DatabaseField parentField : relationshipMapping.getForeignKeyFields()) {
269 String parentFieldName = getPropertyNameFromDatabaseColumnName(rd.getDeclaringType(),
270 parentField.getName());
271 if (parentFieldName != null) {
272 DatabaseField childField = relationshipMapping.getSourceToTargetKeyFields().get(parentField);
273 if (childField != null) {
274
275
276
277 String childFieldName = getPropertyNameFromDatabaseColumnName(referencedEntityType,
278 childField.getName());
279 if (childFieldName != null) {
280 attributeRelationships
281 .add(new DataObjectAttributeRelationshipImpl(parentFieldName, childFieldName));
282 }
283 } else {
284 LOG.warn("Unable to find child field reference. There may be a JPA mapping problem on "
285 + rd.getDeclaringType().getJavaType() + ": " + relationship);
286 }
287 }
288 }
289 relationship.setAttributeRelationships(attributeRelationships);
290
291 populateInverseRelationship(relationshipMapping, relationship);
292
293 } else {
294
295 relationship.setReadOnly(persistentAttributeType == PersistentAttributeType.MANY_TO_ONE);
296 relationship.setSavedWithParent(persistentAttributeType == PersistentAttributeType.ONE_TO_ONE);
297 relationship.setDeletedWithParent(persistentAttributeType == PersistentAttributeType.ONE_TO_ONE);
298 relationship.setLoadedAtParentLoadTime(true);
299 relationship.setLoadedDynamicallyUponUse(false);
300 }
301 }
302
303
304
305
306
307
308
309 protected void populateInverseRelationship(DatabaseMapping mapping, MetadataChildBase relationship) {
310 DatabaseMapping relationshipPartner = findRelationshipPartner(mapping);
311 if (relationshipPartner != null) {
312 Class<?> partnerType = relationshipPartner.getDescriptor().getJavaClass();
313 DataObjectMetadata partnerMetadata = masterMetadataMap.get(partnerType);
314
315
316 if (partnerMetadata != null) {
317
318 MetadataChildBase relationshipPartnerMetadata =
319 (MetadataChildBase)partnerMetadata.getRelationship(relationshipPartner.getAttributeName());
320 if (relationshipPartnerMetadata == null) {
321 relationshipPartnerMetadata =
322 (MetadataChildBase)partnerMetadata.getCollection(relationshipPartner.getAttributeName());
323 }
324 if (relationshipPartnerMetadata != null) {
325 relationshipPartnerMetadata.setInverseRelationship(relationship);
326 relationship.setInverseRelationship(relationshipPartnerMetadata);
327 }
328
329 }
330 }
331 }
332
333
334
335
336
337
338
339 protected DatabaseMapping findRelationshipPartner(DatabaseMapping databaseMapping) {
340 if (databaseMapping instanceof OneToManyMapping) {
341 OneToManyMapping mapping = (OneToManyMapping)databaseMapping;
342 if (mapping.getMappedBy() != null) {
343 Class<?> referenceClass = mapping.getReferenceClass();
344 ClassDescriptor referenceClassDescriptor = getClassDescriptor(referenceClass);
345 return referenceClassDescriptor.getMappingForAttributeName(mapping.getMappedBy());
346 }
347 } else if (databaseMapping instanceof ManyToOneMapping) {
348
349
350
351
352
353
354 ManyToOneMapping mapping = (ManyToOneMapping)databaseMapping;
355 Class<?> referenceClass = mapping.getReferenceClass();
356 ClassDescriptor referenceClassDescriptor = getClassDescriptor(referenceClass);
357
358 for (DatabaseMapping referenceMapping : referenceClassDescriptor.getMappings()) {
359 if (referenceMapping instanceof OneToManyMapping) {
360 OneToManyMapping oneToManyMapping = (OneToManyMapping)referenceMapping;
361 if (mapping.getAttributeName().equals(oneToManyMapping.getMappedBy())) {
362 return oneToManyMapping;
363 }
364 }
365 }
366 } else if (databaseMapping instanceof OneToOneMapping) {
367 OneToOneMapping mapping = (OneToOneMapping)databaseMapping;
368
369
370 ClassDescriptor referenceClassDescriptor = getClassDescriptor(mapping.getReferenceClass());
371
372 for (DatabaseMapping referenceMapping : referenceClassDescriptor.getMappings()) {
373 if (referenceMapping instanceof OneToOneMapping) {
374 OneToOneMapping oneToOneMapping = (OneToOneMapping)referenceMapping;
375 if (oneToOneMapping.getReferenceClass().equals(mapping.getDescriptor().getJavaClass())) {
376 return oneToOneMapping;
377 }
378 }
379 }
380 }
381
382 return null;
383 }
384
385
386
387
388 @Override
389 public DataObjectRelationship addExtensionRelationship(Class<?> entityClass, String extensionPropertyName,
390 Class<?> extensionEntityClass) {
391 ClassDescriptor entityDescriptor = getClassDescriptor(entityClass);
392 ClassDescriptor extensionEntityDescriptor = getClassDescriptor(extensionEntityClass);
393
394 if (LOG.isDebugEnabled()) {
395 LOG.debug("About to attempt to inject a 1:1 relationship on PKs between " + entityDescriptor + " and "
396 + extensionEntityDescriptor);
397 }
398 OneToOneMapping dm = (OneToOneMapping) entityDescriptor.newOneToOneMapping();
399 dm.setAttributeName(extensionPropertyName);
400 dm.setReferenceClass(extensionEntityClass);
401 dm.setDescriptor(entityDescriptor);
402 dm.setIsPrivateOwned(true);
403 dm.setJoinFetch(ForeignReferenceMapping.OUTER_JOIN);
404 dm.setCascadeAll(true);
405 dm.setIsLazy(false);
406 dm.dontUseIndirection();
407 dm.setIsOneToOneRelationship(true);
408 dm.setRequiresTransientWeavedFields(false);
409
410 OneToOneMapping inverse = findExtensionInverse(extensionEntityDescriptor, entityClass);
411 dm.setMappedBy(inverse.getAttributeName());
412 for (DatabaseField sourceField : inverse.getSourceToTargetKeyFields().keySet()) {
413 DatabaseField targetField = inverse.getSourceToTargetKeyFields().get(sourceField);
414
415 dm.addTargetForeignKeyField(sourceField, targetField);
416 }
417
418 dm.preInitialize(getEclipseLinkEntityManager().getDatabaseSession());
419 dm.initialize(getEclipseLinkEntityManager().getDatabaseSession());
420 entityDescriptor.addMapping(dm);
421 entityDescriptor.getObjectBuilder().initialize(getEclipseLinkEntityManager().getDatabaseSession());
422
423
424 ManagedTypeImpl<?> managedType = (ManagedTypeImpl<?>)getEntityManager().getMetamodel().managedType(entityClass);
425 SingularAttributeImpl<?, ?> singularAttribute = new SingularAttributeLocal(managedType, dm);
426 return getRelationshipMetadata(singularAttribute);
427 }
428
429
430
431
432 class SingularAttributeLocal extends SingularAttributeImpl {
433
434
435
436
437
438
439
440 SingularAttributeLocal(ManagedTypeImpl managedType, DatabaseMapping mapping) {
441 super(managedType, mapping);
442 }
443 }
444
445
446
447
448
449
450
451
452 protected OneToOneMapping findExtensionInverse(ClassDescriptor extensionEntityDescriptor, Class<?> entityType) {
453 Collection<DatabaseMapping> derivedIdMappings = extensionEntityDescriptor.getDerivesIdMappinps();
454 String extensionInfo = "(" + extensionEntityDescriptor.getJavaClass().getName() + " -> " + entityType.getName()
455 + ")";
456 if (derivedIdMappings == null || derivedIdMappings.isEmpty()) {
457 throw new MetadataConfigurationException("Attempting to use extension framework, but extension "
458 + extensionInfo + " does not have a valid inverse OneToOne Id mapping back to the extended data "
459 + "object. Please ensure it is annotated property for use of the extension framework with JPA.");
460 } else if (derivedIdMappings.size() > 1) {
461 throw new MetadataConfigurationException("When attempting to determine the inverse relationship for use "
462 + "with extension framework " + extensionInfo + " encountered more than one 'derived id' mapping, "
463 + "there should be only one!");
464 }
465 DatabaseMapping inverseMapping = derivedIdMappings.iterator().next();
466 if (!(inverseMapping instanceof OneToOneMapping)) {
467 throw new MetadataConfigurationException("Identified an inverse derived id mapping for extension "
468 + "relationship " + extensionInfo + " but it was not a one-to-one mapping: " + inverseMapping);
469 }
470 return (OneToOneMapping)inverseMapping;
471 }
472
473
474
475
476
477
478
479 protected ClassDescriptor getClassDescriptor(Class<?> entityClass) {
480 return getEclipseLinkEntityManager().getDatabaseSession().getDescriptor(entityClass);
481 }
482
483
484
485
486
487 protected JpaEntityManager getEclipseLinkEntityManager() {
488 return (JpaEntityManager) entityManager;
489 }
490 }