| /* |
| * 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 |
| package org.eclipse.persistence.internal.indirection; |
| |
| import java.io.Serializable; |
| import java.util.Map; |
| |
| import org.eclipse.persistence.exceptions.DescriptorException; |
| import org.eclipse.persistence.exceptions.IntegrityChecker; |
| import org.eclipse.persistence.indirection.ValueHolderInterface; |
| import org.eclipse.persistence.internal.descriptors.DescriptorIterator; |
| import org.eclipse.persistence.internal.identitymaps.CacheKey; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.internal.sessions.MergeManager; |
| import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; |
| import org.eclipse.persistence.internal.sessions.remote.RemoteValueHolder; |
| import org.eclipse.persistence.mappings.CollectionMapping; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.ForeignReferenceMapping; |
| import org.eclipse.persistence.mappings.ObjectReferenceMapping; |
| import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping; |
| import org.eclipse.persistence.queries.ObjectLevelReadQuery; |
| import org.eclipse.persistence.queries.ReadObjectQuery; |
| import org.eclipse.persistence.queries.ReadQuery; |
| import org.eclipse.persistence.sessions.remote.DistributedSession; |
| |
| /** |
| * <h2>Purpose</h2>: |
| * <p> |
| * An IndirectionPolicy acts as a 'rules' holder that determines |
| * the behavior of a ForeignReferenceMapping (or TransformationMapping) |
| * with respect to indirection, or lack thereof. |
| * </p> |
| * <h3>Description</h3>: |
| * <p> |
| * IndirectionPolicy is an abstract class that defines the protocol to be implemented by |
| * subclasses so that the assorted DatabaseMappings can use an assortment of |
| * indirection policies:</p><ul> |
| * <li> no indirection policy (read everything from database) |
| * <li> basic indirection policy (use ValueHolders) |
| * <li> transparent indirection policy (collections only) |
| * <li> proxy indirection policy (transparent 1:1 indirection using JDK 1.3's <CODE>Proxy</CODE>) |
| * </ul> |
| * |
| * <h3>Responsibilities</h3>: |
| * <ul> |
| * <li>instantiate the various IndirectionPolicies |
| * </ul> |
| * |
| * @see ForeignReferenceMapping |
| * @author Mike Norman |
| * @since TOPLink/Java 2.5 |
| */ |
| public abstract class IndirectionPolicy implements Cloneable, Serializable { |
| protected DatabaseMapping mapping; |
| |
| /** |
| * INTERNAL: |
| * Construct a new indirection policy. |
| */ |
| protected IndirectionPolicy() { |
| super(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return a backup clone of the attribute. |
| */ |
| public Object backupCloneAttribute(Object attributeValue, Object clone, Object backup, UnitOfWorkImpl unitOfWork) { |
| return this.mapping.buildBackupCloneForPartObject(attributeValue, clone, backup, unitOfWork); |
| } |
| |
| /** |
| * INTERNAL |
| * Return true if the refresh should refresh on this mapping or not. |
| */ |
| protected ReadObjectQuery buildCascadeQuery(MergeManager mergeManager) { |
| ReadObjectQuery query = new ReadObjectQuery(); |
| if (this.mapping.getReferenceDescriptor() != null) { |
| query.setReferenceClass(this.mapping.getReferenceDescriptor().getJavaClass()); |
| } |
| if (mergeManager.shouldCascadeAllParts()) { |
| query.cascadeAllParts(); |
| query.refreshIdentityMapResult(); |
| } |
| if (mergeManager.shouldCascadePrivateParts() && getForeignReferenceMapping().isPrivateOwned()) { |
| query.cascadePrivateParts(); |
| query.refreshIdentityMapResult(); |
| } |
| |
| return query; |
| } |
| |
| /** |
| * INTERNAL: This method can be used when an Indirection Object is required |
| * to be built from a provided ValueHolderInterface object. This may be used |
| * for custom value holder types. Certain policies like the |
| * TransparentIndirectionPolicy may wrap the valueholder in another object. |
| */ |
| |
| public abstract Object buildIndirectObject(ValueHolderInterface valueHolder); |
| |
| /** |
| * INTERNAL: |
| * Clones itself. |
| */ |
| @Override |
| public Object clone() { |
| try { |
| return super.clone(); |
| } catch (CloneNotSupportedException e) { |
| throw new InternalError(); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return a clone of the attribute. |
| * @param buildDirectlyFromRow indicates that we are building the clone |
| * directly from a row as opposed to building the original from the |
| * row, putting it in the shared cache, and then cloning the original. |
| */ |
| public abstract Object cloneAttribute(Object attributeValue, Object original, CacheKey cacheKey, Object clone, Integer refreshCascade, AbstractSession cloningSession, boolean buildDirectlyFromRow); |
| |
| /** |
| * INTERNAL: |
| * Return the primary key for the reference object (i.e. the object |
| * object referenced by domainObject and specified by mapping). |
| * This key will be used by a RemoteValueHolder. |
| */ |
| public Object extractPrimaryKeyForReferenceObject(Object referenceObject, AbstractSession session) { |
| return getOneToOneMapping().extractPrimaryKeysFromRealReferenceObject(referenceObject, session); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the reference row for the reference object. |
| * This allows the new row to be built without instantiating |
| * the reference object. |
| * Return null if the object has already been instantiated. |
| */ |
| public abstract AbstractRecord extractReferenceRow(Object referenceObject); |
| |
| /** |
| * INTERNAL: |
| * An object has been serialized from the server to the client. |
| * Replace the transient attributes of the remote value holders |
| * with client-side objects. |
| */ |
| public abstract void fixObjectReferences(Object object, Map objectDescriptors, Map processedObjects, ObjectLevelReadQuery query, DistributedSession session); |
| |
| /** |
| * INTERNAL: |
| * Reduce casting clutter.... |
| */ |
| protected CollectionMapping getCollectionMapping() { |
| return (CollectionMapping)this.mapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Reduce casting clutter.... |
| */ |
| protected ForeignReferenceMapping getForeignReferenceMapping() { |
| return (ForeignReferenceMapping)this.mapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the database mapping that uses the indirection policy. |
| */ |
| public DatabaseMapping getMapping() { |
| return mapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Reduce casting clutter.... |
| */ |
| protected ObjectReferenceMapping getOneToOneMapping() { |
| return (ObjectReferenceMapping)this.mapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the original indirection object for a unit of work indirection object. |
| */ |
| public abstract Object getOriginalIndirectionObject(Object unitOfWorkIndirectionObject, AbstractSession session); |
| |
| /** |
| * INTERNAL: |
| * Return the original indirection object for a unit of work indirection object. |
| */ |
| public Object getOriginalIndirectionObjectForMerge(Object unitOfWorkIndirectionObject, AbstractSession session){ |
| return getOriginalIndirectionObject(unitOfWorkIndirectionObject, session); |
| } |
| |
| /** |
| * INTERNAL: Return the original valueHolder object. Access to the |
| * underlying valueholder may be required when serializing the valueholder |
| * or converting the valueHolder to another type. |
| */ |
| public abstract Object getOriginalValueHolder(Object unitOfWorkIndirectionObject, AbstractSession session); |
| |
| /** |
| * INTERNAL: |
| * Return the "real" attribute value, as opposed to any wrapper. |
| * This will trigger the wrapper to instantiate the value. |
| */ |
| public abstract Object getRealAttributeValueFromObject(Object object, Object attribute); |
| |
| /** |
| * INTERNAL: |
| * Trigger the instantiation of the value. |
| */ |
| public void instantiateObject(Object object, Object attribute) { |
| getRealAttributeValueFromObject(object, attribute); |
| } |
| |
| /** |
| * INTERNAL: |
| * Reduce casting clutter.... |
| */ |
| protected AbstractTransformationMapping getTransformationMapping() { |
| return (AbstractTransformationMapping)this.mapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Extract and return the appropriate value from the |
| * specified remote value holder. |
| */ |
| public abstract Object getValueFromRemoteValueHolder(RemoteValueHolder remoteValueHolder); |
| |
| /** |
| * INTERNAL: |
| * The method validateAttributeOfInstantiatedObject(Object attributeValue) fixes the value of the attributeValue |
| * in cases where it is null and indirection requires that it contain some specific data structure. Return whether this will happen. |
| * This method is used to help determine if indirection has been triggered |
| * @param attributeValue |
| * @return |
| * @see validateAttributeOfInstantiatedObject(Object attributeValue) |
| */ |
| public boolean isAttributeValueFullyBuilt(Object attributeValue){ |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize the indirection policy (Do nothing by default) |
| */ |
| public void initialize() { |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public boolean isWeavedObjectBasicIndirectionPolicy() { |
| return false; |
| } |
| |
| /** |
| * INTERNAL: |
| * Iterate over the specified attribute value, |
| * heeding the settings in the iterator. |
| */ |
| public void iterateOnAttributeValue(DescriptorIterator iterator, Object attributeValue) { |
| if (attributeValue != null) { |
| this.mapping.iterateOnRealAttributeValue(iterator, attributeValue); |
| } |
| } |
| |
| /** |
| * INTERNAL |
| * Replace the client value holder with the server value holder, |
| * after copying some of the settings from the client value holder. |
| */ |
| protected void mergeClientIntoServerValueHolder(RemoteValueHolder serverValueHolder, MergeManager mergeManager) { |
| serverValueHolder.setMapping(this.mapping); |
| serverValueHolder.setSession(mergeManager.getSession()); |
| |
| if (this.mapping.isForeignReferenceMapping()) { |
| ObjectLevelReadQuery query = buildCascadeQuery(mergeManager); |
| serverValueHolder.setQuery(query); |
| } |
| } |
| |
| /** |
| * INTERNAL |
| * Replace the client value holder with the server value holder, |
| * after copying some of the settings from the client value holder. |
| */ |
| public abstract void mergeRemoteValueHolder(Object clientSideDomainObject, Object serverSideDomainObject, MergeManager mergeManager); |
| |
| /** |
| * INTERNAL: |
| * Return the null value of the appropriate attribute. That is, the |
| * field from the database is NULL, return what should be |
| * placed in the object's attribute as a result. |
| */ |
| public abstract Object nullValueFromRow(); |
| |
| /** |
| * INTERNAL: |
| * Return whether the specified object is instantiated. |
| */ |
| public abstract boolean objectIsInstantiated(Object object); |
| |
| /** |
| * INTERNAL: |
| * Return whether the specified object can be instantiated without database access. |
| */ |
| public abstract boolean objectIsEasilyInstantiated(Object object); |
| |
| /** |
| * INTERNAL: |
| * Return whether the specified object is instantiated, or if it has changes. |
| */ |
| public boolean objectIsInstantiatedOrChanged(Object object) { |
| return objectIsInstantiated(object); |
| } |
| |
| /** |
| * INTERNAL: |
| * set the database mapping that uses the indirection policy. |
| */ |
| public void setMapping(DatabaseMapping mapping) { |
| this.mapping = mapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the value of the appropriate attribute of target to attributeValue. |
| * In this case, simply place the value inside the target. |
| */ |
| public void setRealAttributeValueInObject(Object target, Object attributeValue) { |
| this.mapping.setAttributeValueInObject(target, attributeValue); |
| } |
| |
| /** |
| * INTERNAL: |
| * Same functionality as setRealAttributeValueInObject(Object target, Object attributeValue) but allows |
| * overridden behavior for IndirectionPolicies that track changes |
| * @param target |
| * @param attributeValue |
| * @param allowChangeTracking |
| */ |
| public void setRealAttributeValueInObject(Object target, Object attributeValue, boolean allowChangeTracking) { |
| setRealAttributeValueInObject(target, attributeValue); |
| } |
| |
| /** |
| * INTERNAL: |
| * set the source object into QueryBasedValueHolder. |
| * Used only by transparent indirection. |
| */ |
| public void setSourceObject(Object sourceObject, Object attributeValue) { |
| } |
| |
| /** |
| * ADVANCED: |
| * This method will only change the behavior of TransparentIndirectionPolicy. |
| * |
| * IndirectList and IndirectSet can be configured not to instantiate the list from the |
| * database when you add and remove from them. IndirectList defaults to this behavior. When |
| * Set to true, the collection associated with this TransparentIndirection will be setup so as |
| * not to instantiate for adds and removes. The weakness of this setting for an IndirectSet is |
| * that when the set is not instantiated, if a duplicate element is added, it will not be |
| * detected until commit time. |
| * |
| */ |
| public void setUseLazyInstantiation(Boolean useLazyInstantiation) { |
| } |
| |
| /** |
| * ADVANCED: |
| * Returns false unless this is a transparent indirection policy |
| * |
| * IndirectList and IndirectSet can be configured not to instantiate the list from the |
| * database when you add and remove from them. IndirectList defaults to this behavior. When |
| * Set to true, the collection associated with this TransparentIndirection will be setup so as |
| * not to instantiate for adds and removes. The weakness of this setting for an IndirectSet is |
| * that when the set is not instantiated, if a duplicate element is added, it will not be |
| * detected until commit time. |
| */ |
| public Boolean shouldUseLazyInstantiation() { |
| return false; |
| } |
| /** |
| * Reset the wrapper used to store the value. |
| * This is only required if a wrapper is used. |
| */ |
| public void reset(Object target) { |
| // Nothing by default. |
| } |
| |
| /** |
| * INTERNAL: |
| * Return whether the indirection policy actually uses indirection. |
| * The default is true. |
| */ |
| public boolean usesIndirection() { |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return whether the indirection policy uses transparent indirection. |
| * The default is false. |
| */ |
| public boolean usesTransparentIndirection(){ |
| return false; |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that the value of the attribute within an instantiated object |
| * is of the appropriate type for the indirection policy. |
| * If it is incorrect, throw an exception. |
| * If the value is null return a new indirection object to be used for the attribute. |
| */ |
| public Object validateAttributeOfInstantiatedObject(Object attributeValue) throws DescriptorException { |
| return attributeValue; |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that the container policy is compatible with the |
| * indirection policy. If it is incorrect, add an exception to the |
| * integrity checker. |
| */ |
| public void validateContainerPolicy(IntegrityChecker checker) throws DescriptorException { |
| // by default, do nothing |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that attributeType is correct for the |
| * indirection policy. If it is incorrect, add an exception to the |
| * integrity checker. |
| */ |
| public void validateDeclaredAttributeType(Class attributeType, IntegrityChecker checker) throws DescriptorException { |
| // by default, do nothing |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that attributeType is an appropriate collection type for the |
| * indirection policy. If it is incorrect, add an exception to the integrity checker. |
| */ |
| public void validateDeclaredAttributeTypeForCollection(Class attributeType, IntegrityChecker checker) throws DescriptorException { |
| // by default, do nothing |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that getter returnType is correct for the |
| * indirection policy. If it is incorrect, add an exception |
| * to the integrity checker. |
| */ |
| public void validateGetMethodReturnType(Class returnType, IntegrityChecker checker) throws DescriptorException { |
| // by default, do nothing |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that getter returnType is an appropriate collection type for the |
| * indirection policy. If it is incorrect, add an exception to the integrity checker. |
| */ |
| public void validateGetMethodReturnTypeForCollection(Class returnType, IntegrityChecker checker) throws DescriptorException { |
| // by default, do nothing |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that setter parameterType is correct for the |
| * indirection policy. If it is incorrect, add an exception |
| * to the integrity checker. |
| */ |
| public void validateSetMethodParameterType(Class parameterType, IntegrityChecker checker) throws DescriptorException { |
| // by default, do nothing |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that setter parameterType is an appropriate collection type for the |
| * indirection policy. If it is incorrect, add an exception to the integrity checker. |
| */ |
| public void validateSetMethodParameterTypeForCollection(Class parameterType, IntegrityChecker checker) throws DescriptorException { |
| // by default, do nothing |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the value to be stored in the object's attribute. |
| * This value is determined by the batchQuery. |
| */ |
| public abstract Object valueFromBatchQuery(ReadQuery batchQuery, AbstractRecord row, ObjectLevelReadQuery originalQuery, CacheKey parentCacheKey); |
| |
| /** |
| * INTERNAL: |
| * Return the value to be stored in the object's attribute. |
| * This value is determined by invoking the appropriate |
| * method on the object and passing it the row and session. |
| */ |
| public abstract Object valueFromMethod(Object object, AbstractRecord row, AbstractSession session); |
| |
| /** |
| * INTERNAL: |
| * Return the value to be stored in the object's attribute. |
| * This value is determined by the query. |
| */ |
| public abstract Object valueFromQuery(ReadQuery query, AbstractRecord row, Object sourceObject, AbstractSession session); |
| |
| /** |
| * INTERNAL: |
| * Return the value to be stored in the object's attribute. |
| * This value is determined by the query. |
| */ |
| public abstract Object valueFromQuery(ReadQuery query, AbstractRecord row, AbstractSession session); |
| |
| /** |
| * INTERNAL: |
| * Return the value to be stored in the object's attribute. |
| * This value is determined by the row. |
| */ |
| public abstract Object valueFromRow(Object object); |
| } |