| /* |
| * 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. |
| * @param aDescriptor |
| * @param field |
| * @param allMappings |
| */ |
| 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. |
| * @param aDescriptor |
| * @param field |
| * @param allMappings |
| */ |
| 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. |
| * @param classLoader |
| */ |
| @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: |
| * @param cls |
| * @param fieldName |
| * @return the field from the class with name equal to fieldName. |
| * @throws NoSuchFieldException |
| */ |
| 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); |
| } |
| } |