blob: b00b5e1fc9ac0af1cb1c852088d4db986b449177 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// 02/02/2009-2.0 Chris delahunt
// - 241765: JPA 2.0 Derived identities
// 04/24/2009-2.0 Guy Pelletier
// - 270011: JPA 2.0 MappedById support
// 10/21/2009-2.0 Guy Pelletier
// - 290567: mappedbyid support incomplete
// 12/17/2010-2.2 Guy Pelletier
// - 330755: Nested embeddables can't be used as embedded ids
// 11/10/2011-2.4 Guy Pelletier
// - 357474: Address primaryKey option from tenant discriminator column
// 14/05/2012-2.4 Guy Pelletier
// - 376603: Provide for table per tenant support for multitenant applications
// 08/20/2012-2.4 Guy Pelletier
// - 381079: EclipseLink dynamic entity does not support embedded-id
package org.eclipse.persistence.internal.jpa;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.persistence.annotations.CacheKeyType;
import org.eclipse.persistence.descriptors.CMPPolicy;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.identitymaps.CacheId;
import org.eclipse.persistence.internal.indirection.WeavedObjectBasicIndirectionPolicy;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
import org.eclipse.persistence.internal.security.PrivilegedGetField;
import org.eclipse.persistence.internal.security.PrivilegedGetMethod;
import org.eclipse.persistence.internal.security.PrivilegedGetValueFromField;
import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker;
import org.eclipse.persistence.internal.security.PrivilegedSetValueInField;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.ObjectReferenceMapping;
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.eclipse.persistence.mappings.foundation.AbstractColumnMapping;
/**
* Defines primary key extraction code for use in JPA. A descriptor should have a CMP3Policy
* attached to handle basic Id as well as IdClass/EmbeddedId usage.
*
* @since TopLink 10.1.3
*/
public class CMP3Policy extends CMPPolicy {
/** Stores the fields for this classes compound primary key class if required. */
protected transient KeyElementAccessor[] keyClassFields;
/** Used to look up the KeyElementAccessor for a specific DatabaseField, used for
resolving DerivedIds */
protected transient HashMap<DatabaseField,KeyElementAccessor> fieldToAccessorMap;
// Store the primary key class name
protected String pkClassName;
// Stores the class version of the PKClass
protected Class pkClass = null;
public CMP3Policy() {
super();
}
/**
* INTERNAL:
* Add the read only mappings for the given field to the allMappings list.
*/
protected void addWritableMapping(ClassDescriptor aDescriptor, DatabaseField field, List allMappings) {
DatabaseMapping writableMapping = aDescriptor.getObjectBuilder().getMappingForField(field);
if (writableMapping != null) {
// Since it may be another aggregate mapping, add it to
// the allMappings list so we can drill down on it as
// well.
allMappings.add(writableMapping);
}
}
/**
* INTERNAL:
* Add the writable mapping for the given field to the allMappings list.
*/
protected void addReadOnlyMappings(ClassDescriptor aDescriptor, DatabaseField field, List allMappings) {
List<DatabaseMapping> readOnlyMappings = aDescriptor.getObjectBuilder().getReadOnlyMappingsForField(field);
if (readOnlyMappings != null) {
allMappings.addAll(readOnlyMappings);
}
}
/**
* INTERNAL:
* Clone the CMP3Policy
*/
@Override
public CMP3Policy clone() {
CMP3Policy policy = new CMP3Policy();
policy.setPrimaryKeyClassName(getPKClassName());
policy.setPKClass(getPKClass());
return policy;
}
/**
* INTERNAL:
* Convert all the class-name-based settings in this object to actual class-based
* settings. This method is used when converting a project that has been built
* with class names to a project with classes.
*/
@Override
public void convertClassNamesToClasses(ClassLoader classLoader){
if(getPKClassName() != null){
try{
Class aPKClass = null;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
aPKClass = AccessController.doPrivileged(new PrivilegedClassForName<>(getPKClassName(), true, classLoader));
} catch (PrivilegedActionException exception) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("pk_class_not_found", new Object[] {this.pkClassName}), exception.getException());
}
} else {
aPKClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(getPKClassName(), true, classLoader);
}
setPKClass(aPKClass);
} catch (ClassNotFoundException exc){
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("pk_class_not_found", new Object[] {this.pkClassName}), exc);
}
}
}
/**
* INTERNAL:
* Return if this policy is for CMP3.
*/
@Override
public boolean isCMP3Policy() {
return true;
}
/**
* INTERNAL:
*/
public void setPrimaryKeyClassName(String pkClassName) {
this.pkClassName = pkClassName;
}
/**
* INTERNAL:
* Return the java Class representing the primary key class name
*/
@Override
public Class getPKClass() {
return this.pkClass;
}
/**
* ADVANCED:
*/
public void setPKClass(Class pkClass) {
this.pkClass = pkClass;
}
/**
* INTERNAL:
*/
public String getPKClassName() {
return pkClassName;
}
/**
* INTERNAL:
*/
@Override
public Object getPKClassInstance() {
try {
return getPKClass().getConstructor().newInstance();
} catch (ReflectiveOperationException ex) {
throw DescriptorException.exceptionAccessingPrimaryKeyInstance(this.getDescriptor(), ex);
}
}
/**
* INTERNAL:
* Use the key to create a EclipseLink primary key.
* If the key is simple (direct mapped) then just add it to a vector,
* otherwise must go through the inefficient process of copying the key into the bean
* and extracting the key from the bean.
*/
@Override
public Object createPrimaryKeyFromId(Object key, AbstractSession session) {
// If the descriptor primary key is mapped through direct-to-field mappings,
// then no elaborate conversion is required.
// If key is compound, add each value to the vector.
KeyElementAccessor[] pkElementArray = this.getKeyClassFields();
Object[] primaryKey = null;
if (getDescriptor().getCacheKeyType() != CacheKeyType.ID_VALUE) {
primaryKey = new Object[pkElementArray.length];
}
for (int index = 0; index < pkElementArray.length; index++) {
DatabaseMapping mapping = pkElementArray[index].getMapping();
Object fieldValue = null;
if (mapping.isAbstractColumnMapping()) {
if (pkElementArray[index].isNestedAccessor()) {
// We have nested aggregate(s) in the embedded id pkclass.
DatabaseField keyField = pkElementArray[index].getDatabaseField();
Object keyToUse = key;
DatabaseMapping keyMapping = getDescriptor().getObjectBuilder().getMappingForField(keyField);
if (keyMapping.isAggregateMapping()) {
keyMapping = keyMapping.getReferenceDescriptor().getObjectBuilder().getMappingForField(keyField);
// Keep driving down the nested aggregates ...
while (keyMapping.isAggregateMapping()) {
keyToUse = keyMapping.getRealAttributeValueFromObject(keyToUse, session);
keyMapping = keyMapping.getReferenceDescriptor().getObjectBuilder().getMappingForField(keyField);
}
fieldValue = ((AbstractColumnMapping)mapping).getFieldValue(pkElementArray[index].getValue(keyToUse, session), session);
} else {
// This should never hit but just in case ... better to get a proper exception rather than a NPE etc.
fieldValue = ((AbstractColumnMapping)mapping).getFieldValue(pkElementArray[index].getValue(keyToUse, session), session);
}
} else {
fieldValue = ((AbstractColumnMapping)mapping).getFieldValue(pkElementArray[index].getValue(key, session), session);
}
} else {
fieldValue = pkElementArray[index].getValue(key, session);
if ( (fieldValue !=null) && (pkClass != null) && (mapping.isOneToOneMapping()) ){
OneToOneMapping refmapping = (OneToOneMapping)mapping;
DatabaseField targetKey = refmapping.getSourceToTargetKeyFields().get(pkElementArray[index].getDatabaseField());
CMPPolicy refPolicy = refmapping.getReferenceDescriptor().getCMPPolicy();
if (refPolicy.isCMP3Policy()){
Class<Object> aPKClass = refPolicy.getPKClass();
if ((aPKClass != null) && (aPKClass != fieldValue.getClass()) && (!aPKClass.isAssignableFrom(fieldValue.getClass()))) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("invalid_pk_class", new Object[] { aPKClass, fieldValue.getClass() }));
}
fieldValue = ((CMP3Policy)refPolicy).getPkValueFromKeyForField(fieldValue, targetKey, session);
}
}
}
if (getDescriptor().getCacheKeyType() == CacheKeyType.ID_VALUE) {
return fieldValue;
}
primaryKey[index] = fieldValue;
}
return new CacheId(primaryKey);
}
/**
* INTERNAL:
* @return the field from the class with name equal to fieldName.
*/
protected Field getField(Class cls, String fieldName) throws NoSuchFieldException {
Field keyField = null;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
keyField = AccessController.doPrivileged(new PrivilegedGetField(cls, fieldName, true));
} catch (PrivilegedActionException exception) {
throw (NoSuchFieldException) exception.getException();
}
} else {
keyField = PrivilegedAccessHelper.getField(cls, fieldName, true);
}
return keyField;
}
/**
* INTERNAL:
* Use the key to create a bean and initialize its primary key fields.
* Note: If is a compound PK then a primary key object is being used.
* This method should only be used for 'templates' when executing
* queries. The bean built will not be given an EntityContext and should
* not be used as an actual entity bean.
*
* @param key Object the primary key to use for initializing the bean's
* corresponding pk fields
* @return Object
*/
@Override
public Object createBeanUsingKey(Object key, AbstractSession session) {
try {
Object bean = this.getDescriptor().getInstantiationPolicy().buildNewInstance();
KeyElementAccessor[] keyElements = this.getKeyClassFields();
for (int index = 0; index < keyElements.length; ++index) {
Object toWriteInto = bean;
Object keyFieldValue = keyElements[index].getValue(key, session);
DatabaseField field = keyElements[index].getDatabaseField();
DatabaseMapping mapping = this.getDescriptor().getObjectBuilder().getMappingForAttributeName(keyElements[index].getAttributeName());
if (mapping == null) {// must be aggregate
mapping = this.getDescriptor().getObjectBuilder().getMappingForField(field);
}
while (mapping.isAggregateObjectMapping()) {
Object aggregate = mapping.getRealAttributeValueFromObject(toWriteInto, session);
if (aggregate == null) {
aggregate = mapping.getReferenceDescriptor().getJavaClass().getConstructor().newInstance();
mapping.setRealAttributeValueInObject(toWriteInto, aggregate);
}
mapping = mapping.getReferenceDescriptor().getObjectBuilder().getMappingForAttributeName(keyElements[index].getAttributeName());
if (mapping == null) {// must be aggregate
mapping = this.getDescriptor().getObjectBuilder().getMappingForField(field);
}
//change the object to write into to the aggregate for the next stage of the
// loop or for when we exit the loop.
toWriteInto = aggregate;
}
mapping.setRealAttributeValueInObject(toWriteInto, keyFieldValue);
}
return bean;
} catch (Exception e) {
throw DescriptorException.errorUsingPrimaryKey(key, this.getDescriptor(), e);
}
}
/**
* INTERNAL:
* Cache the bean's primary key fields so speed up creating of primary key
* objects and initialization of beans.
*
* Note, we have to re-look up the fields for the bean class since
* these fields may have been loaded with the wrong loader (thank you Kirk).
* If the key is compound, we also have to look up the fields for the key.
*/
protected KeyElementAccessor[] initializePrimaryKeyFields(Class keyClass, AbstractSession session) {
KeyElementAccessor[] pkAttributes = null;
ClassDescriptor aDescriptor = this.getDescriptor();
fieldToAccessorMap = new HashMap<DatabaseField,KeyElementAccessor>();
int numberOfIDFields = aDescriptor.getPrimaryKeyFields().size();
pkAttributes = new KeyElementAccessor[numberOfIDFields];
Iterator<DatabaseField> attributesIter = aDescriptor.getPrimaryKeyFields().iterator();
// Used fields in case it is an embedded class
for (int i = 0; attributesIter.hasNext(); i++) {
DatabaseField field = attributesIter.next();
// We need to check all mappings for this field, not just the writable one and instead of
// having multiple sections of duplicate code we'll just add the writable mapping directly
// to the list.
List allMappings = new ArrayList(1);
addReadOnlyMappings(aDescriptor, field, allMappings);
addWritableMapping(aDescriptor, field, allMappings);
// This exception will be used to determine if the element (field or method) from
// the mapping was found on the key class was found or not and throw an exception otherwise.
Exception noSuchElementException = null;
// Set the current key class ...
Class currentKeyClass = keyClass;
// We always start by looking at the writable mappings first. Our preference is to use the
// writable mappings unless a derived id mapping is specified in which case we'll want to use
// that mapping instead when we find it.
for (int index = (allMappings.size() - 1); index >= 0; --index) {
DatabaseMapping mapping = (DatabaseMapping) allMappings.get(index);
// So here is the ugly check to see if we want to further look at this mapping as part of
// the id or not.
if (aDescriptor.hasDerivedId() && ! mapping.derivesId()) {
// If the mapping is not a derived id, then we need to keep looking for the mapping that
// is marked as the derived id mapping. However, in a mapped by id case, we may have
// 'extra' non-derived id fields within the embeddable class (and the writable mapping
// that we care about at this point will be defined on the embeddable descriptor). Therefore,
// we can't bail at this point and must drill down further into the embeddable to make sure
// we initialize this portion of the composite id.
if (mapping.isAggregateMapping() && allMappings.size() > 1) {
// Bail ... more mappings to check.
continue;
}
} else if (mapping.isForeignReferenceMapping() && !mapping.isOneToOneMapping()) {
// JPA 2.0 allows DerrivedIds, so Id fields are either OneToOne or DirectToField mappings
continue;
}
if (mapping.isAggregateMapping()) {
// Either this aggregate is the key class, or we need to drill down further. Add the read
// only and writable mappings from the aggregate.
addReadOnlyMappings(mapping.getReferenceDescriptor(), field, allMappings);
addWritableMapping(mapping.getReferenceDescriptor(), field, allMappings);
// Since we added the mappings from this aggregate mapping, we should remove this aggregate
// mapping from the allMappings list. Otherwise, if the mapping for the primary key field is
// not found in the aggregate (or nested aggregate) then we will hit an infinite loop when
// searching the aggregate and its mappings. Note: This is cautionary, since in reality, this
// 'should' never happen, but if it does we certainly would rather throw an exception instead
// of causing an infinite loop.
allMappings.remove(mapping);
// Update the index to parse the next mapping correctly.
index = allMappings.size();
// Update the key class now ...
currentKeyClass = mapping.getReferenceDescriptor().getJavaClass();
} else {
String fieldName = (mapping.hasMapsIdValue()) ? mapping.getMapsIdValue() : mapping.getAttributeName();
if (currentKeyClass == null || mapping.isMultitenantPrimaryKeyMapping()) {
// Without a currentKeyClass, the primary key is a non compound key but
// we may need to add any multitenant primary key mappings that are
// defined and we need an accessor for them. The same case will hold
// true when we do have a currentKeyClass. Multitenant primary keys
// must still be added.
pkAttributes[i] = new KeyIsElementAccessor(fieldName, field, mapping);
if (mapping.isAbstractDirectMapping()) {
setPKClass(ConversionManager.getObjectClass(mapping.getAttributeClassification()));
} else if (mapping.isOneToOneMapping()) {
ClassDescriptor refDescriptor = mapping.getReferenceDescriptor();
// ensure the referenced descriptor was initialized
if (!session.isRemoteSession()) {
refDescriptor.initialize(session);
}
CMPPolicy refPolicy = refDescriptor.getCMPPolicy();
setPKClass(refPolicy.getPKClass());
}
fieldToAccessorMap.put(field, pkAttributes[i]);
noSuchElementException = null;
} else {
if (mapping.isOneToOneMapping()){
ClassDescriptor refDescriptor = mapping.getReferenceDescriptor();
// ensure the referenced descriptor was initialized
if (!session.isRemoteSession()) {
refDescriptor.initialize(session);
}
CMPPolicy refPolicy = refDescriptor.getCMPPolicy();
if ((refPolicy!=null) && refPolicy.isCMP3Policy() && (refPolicy.getPKClass() == currentKeyClass)){
//Since the ref pk class is our pk class, get the accessor we need to pull the value out of the PK class for our field
OneToOneMapping refmapping = (OneToOneMapping)mapping;
DatabaseField targetKey = refmapping.getSourceToTargetKeyFields().get(field);
pkAttributes[i] = ((CMP3Policy)refPolicy).fieldToAccessorMap.get(targetKey);
//associate their accessor to our field so we can look it up when we need it
this.fieldToAccessorMap.put(field, pkAttributes[i]);
noSuchElementException = null;
break;
}
}
try {
pkAttributes[i] = new FieldAccessor(this, getField(currentKeyClass, fieldName), fieldName, field, mapping, currentKeyClass != keyClass);
fieldToAccessorMap.put(field, pkAttributes[i]);
noSuchElementException = null;
} catch (NoSuchFieldException ex) {
String getMethodName = null;
String setMethodName = null;
if(mapping.isObjectReferenceMapping() && ((ObjectReferenceMapping)mapping).getIndirectionPolicy().isWeavedObjectBasicIndirectionPolicy()) {
WeavedObjectBasicIndirectionPolicy weavedIndirectionPolicy = (WeavedObjectBasicIndirectionPolicy)((ObjectReferenceMapping)mapping).getIndirectionPolicy();
if (weavedIndirectionPolicy.hasUsedMethodAccess()) {
getMethodName = weavedIndirectionPolicy.getGetMethodName();
setMethodName = weavedIndirectionPolicy.getSetMethodName();
}
} else {
getMethodName = mapping.getGetMethodName();
setMethodName = mapping.getSetMethodName();
}
if (getMethodName != null) {
// Must be a property.
try {
Method getMethod = null;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
getMethod = AccessController.doPrivileged(new PrivilegedGetMethod(currentKeyClass, getMethodName, new Class[] {}, true));
} catch (PrivilegedActionException exception) {
throw (NoSuchMethodException)exception.getException();
}
} else {
getMethod = PrivilegedAccessHelper.getMethod(currentKeyClass, getMethodName, new Class[] {}, true);
}
Method setMethod = null;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
setMethod = AccessController.doPrivileged(new PrivilegedGetMethod(currentKeyClass, setMethodName, new Class[] {getMethod.getReturnType()}, true));
} catch (PrivilegedActionException exception) {
throw (NoSuchMethodException)exception.getException();
}
} else {
setMethod = PrivilegedAccessHelper.getMethod(currentKeyClass, setMethodName, new Class[] {getMethod.getReturnType()}, true);
}
pkAttributes[i] = new PropertyAccessor(this, getMethod, setMethod, fieldName, field, mapping, currentKeyClass != keyClass);
this.fieldToAccessorMap.put(field, pkAttributes[i]);
noSuchElementException = null;
} catch (NoSuchMethodException exs) {
// not a field not a method, but a pk class is defined. Check for other mappings
noSuchElementException = exs;
}
} else {
noSuchElementException = ex;
}
// If we can't load the field or methods and the attribute accessor is a values
// accessor then we're dealing with a dynamic entity.
if (noSuchElementException != null && mapping.getAttributeAccessor().isValuesAccessor()) {
pkAttributes[i] = new ValuesFieldAccessor(fieldName, field, mapping, currentKeyClass != keyClass);
noSuchElementException = null;
}
}
}
if (mapping.derivesId() || noSuchElementException == null) {
// Break out of the loop as we do not need to look for
// any more mappings
break;
}
}
} // end for loop
if (noSuchElementException != null) {
throw DescriptorException.errorUsingPrimaryKey(keyClass, getDescriptor(), noSuchElementException);
}
} // end first for loop
return pkAttributes;
}
/**
* INTERNAL:
* @return Returns the keyClassFields.
*/
@Override
protected KeyElementAccessor[] getKeyClassFields() {
return this.keyClassFields;
}
// Made static for performance reasons.
private static abstract class CommonAccessor implements KeyElementAccessor {
private final String attributeName;
private final DatabaseField databaseField;
protected final DatabaseMapping mapping;
private final boolean isNestedAccessor;
public CommonAccessor(String attributeName, DatabaseField field, DatabaseMapping mapping, boolean isNestedAccessor) {
this.attributeName = attributeName;
this.databaseField = field;
this.mapping = mapping;
this.isNestedAccessor = isNestedAccessor;
}
@Override
public String getAttributeName() {
return this.attributeName;
}
@Override
public DatabaseField getDatabaseField() {
return this.databaseField;
}
@Override
public DatabaseMapping getMapping() {
return this.mapping;
}
@Override
public boolean isNestedAccessor() {
return isNestedAccessor;
}
}
// Made static final for performance reasons.
/**
* INTERNAL:
* This class is used when the key class element is a property
*/
private static final class PropertyAccessor extends CommonAccessor {
private final Method getMethod;
private final Method setMethod;
private final CMPPolicy policy;
public PropertyAccessor(CMPPolicy policy, Method getMethod, Method setMethod, String attributeName, DatabaseField databaseField, DatabaseMapping mapping, boolean isNestedAccessor) {
super(attributeName, databaseField, mapping, isNestedAccessor);
this.getMethod = getMethod;
this.setMethod = setMethod;
this.policy = policy;
}
@Override
public Object getValue(Object object, AbstractSession session) {
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
return AccessController.doPrivileged(new PrivilegedMethodInvoker(this.getMethod, object, new Object[] { }));
} catch (PrivilegedActionException exception) {
Exception throwableException = exception.getException();
if (throwableException instanceof IllegalAccessException) {
throw (IllegalAccessException)throwableException;
} else {
throw (java.lang.reflect.InvocationTargetException)throwableException;
}
}
} else {
return PrivilegedAccessHelper.invokeMethod(this.getMethod, object, new Object[] { });
}
} catch (Exception ex) {
throw DescriptorException.errorUsingPrimaryKey(object, policy.getDescriptor(), ex);
}
}
@Override
public void setValue(Object object, Object value) {
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
AccessController.doPrivileged(new PrivilegedMethodInvoker(this.setMethod, object, new Object[] {value}));
} catch (PrivilegedActionException exception) {
Exception throwableException = exception.getException();
if (throwableException instanceof IllegalAccessException) {
throw (IllegalAccessException)throwableException;
} else {
throw (java.lang.reflect.InvocationTargetException)throwableException;
}
}
} else {
PrivilegedAccessHelper.invokeMethod(this.setMethod, object, new Object[] {value});
}
} catch (Exception ex) {
throw DescriptorException.errorUsingPrimaryKey(object, policy.getDescriptor(), ex);
}
}
}
// Made static final for performance reasons.
/**
* INTERNAL:
* This class will be used when the element of the keyclass is a field
*/
private static final class FieldAccessor extends CommonAccessor {
private final Field field;
private final CMPPolicy policy;
public FieldAccessor(CMPPolicy policy, Field field, String attributeName, DatabaseField databaseField, DatabaseMapping mapping, boolean isNestedAccessor) {
super(attributeName, databaseField, mapping, isNestedAccessor);
this.field = field;
this.policy = policy;
}
@Override
public Object getValue(Object object, AbstractSession session) {
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
return AccessController.doPrivileged(new PrivilegedGetValueFromField(field, object));
} catch (PrivilegedActionException exception) {
throw DescriptorException.errorUsingPrimaryKey(object, policy.getDescriptor(), exception.getException()); }
} else {
return org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getValueFromField(field, object);
}
} catch (Exception ex) {
throw DescriptorException.errorUsingPrimaryKey(object, policy.getDescriptor(), ex);
}
}
@Override
public void setValue(Object object, Object value) {
try {
Field pkField = null;
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
pkField = AccessController.doPrivileged(new PrivilegedGetField(object.getClass(), field.getName(), true));
AccessController.doPrivileged(new PrivilegedSetValueInField(pkField, object, value));
} catch (PrivilegedActionException exception) {
throw DescriptorException.errorUsingPrimaryKey(object, policy.getDescriptor(), exception.getException());
}
} else {
pkField = PrivilegedAccessHelper.getField(object.getClass(), field.getName(), true);
PrivilegedAccessHelper.setValueInField(pkField, object, value);
}
} catch (Exception ex) {
throw DescriptorException.errorUsingPrimaryKey(object, policy.getDescriptor(), ex);
}
}
}
// Made static final for performance reasons.
/**
* INTERNAL:
* This class will be used when the element of the keyclass is a virtual field.
*/
private static final class ValuesFieldAccessor extends CommonAccessor {
public ValuesFieldAccessor(String attributeName, DatabaseField databaseField, DatabaseMapping mapping, boolean isNestedAccessor) {
super(attributeName, databaseField, mapping, isNestedAccessor);
}
@Override
public Object getValue(Object object, AbstractSession session) {
return mapping.getRealAttributeValueFromObject(object, session);
}
@Override
public void setValue(Object object, Object value) {
mapping.setRealAttributeValueInObject(object, value);
}
}
/**
* INTERNAL:
* Pull the value for the field from the key.
*
* @param key Object the primary key to use to get the value for the field
* @param field DatabaseField the field to find a value for
* @return Object
*/
public Object getPkValueFromKeyForField(Object key, DatabaseField field, AbstractSession session){
Object fieldValue = null;
KeyElementAccessor accessor = this.fieldToAccessorMap.get(field);
DatabaseMapping mapping = accessor.getMapping();
if (mapping.isAbstractColumnMapping()) {
fieldValue = ((AbstractColumnMapping)mapping).getFieldValue(accessor.getValue(key, session), session);
} else {
fieldValue = accessor.getValue(key, session);
if (mapping.isOneToOneMapping()){
OneToOneMapping refmapping = (OneToOneMapping)mapping;
DatabaseField targetKey = refmapping.getSourceToTargetKeyFields().get(accessor.getDatabaseField());
CMPPolicy refPolicy = refmapping.getReferenceDescriptor().getCMPPolicy();
if (refPolicy.isCMP3Policy()){
Class<Object> pkClass = refPolicy.getPKClass();
if ((pkClass != null) && (pkClass != fieldValue.getClass()) && (!pkClass.isAssignableFrom(fieldValue.getClass()))) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("invalid_pk_class", new Object[] { refPolicy.getPKClass(), fieldValue.getClass() }));
}
fieldValue = ((CMP3Policy)refPolicy).getPkValueFromKeyForField(fieldValue, targetKey, session);
}
}
}
return fieldValue;
}
/**
* INTERNAL:
* Initialize the CMPPolicy settings.
*/
@Override
public void initialize(ClassDescriptor descriptor, AbstractSession session) throws DescriptorException {
super.initialize(descriptor, session);
this.keyClassFields = initializePrimaryKeyFields(this.pkClass, session);
}
/**
* INTERNAL:
* Initialize the CMPPolicy settings for remote sessions.
*/
@Override
public void remoteInitialize(ClassDescriptor descriptor, AbstractSession session) throws DescriptorException {
super.remoteInitialize(descriptor, session);
this.keyClassFields = initializePrimaryKeyFields(null, session);
}
}