| /* |
| * 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.descriptors.changetracking; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| |
| import java.util.List; |
| |
| import org.eclipse.persistence.internal.descriptors.ObjectBuilder; |
| import org.eclipse.persistence.internal.descriptors.changetracking.ObjectChangeListener; |
| import org.eclipse.persistence.internal.descriptors.changetracking.AggregateObjectChangeListener; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.descriptors.FetchGroupManager; |
| import org.eclipse.persistence.exceptions.DescriptorException; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| |
| /** |
| * PUBLIC: |
| * A ObjectChangeTrackingPolicy allows an object to calculate for itself whether |
| * it should has changed by implementing ChangeTracker. Changed objects will |
| * be processed in the UnitOfWork commit process to include any changes in the results of the |
| * commit. Unchanged objects will be ignored. |
| * @see DeferredChangeDetectionPolicy |
| * @see ChangeTracker |
| */ |
| public class ObjectChangeTrackingPolicy extends DeferredChangeDetectionPolicy { |
| |
| /** |
| * INTERNAL: |
| * This method is used to disable changetracking temporarily |
| */ |
| @Override |
| public void dissableEventProcessing(Object changeTracker) { |
| ObjectChangeListener listener = (ObjectChangeListener)((ChangeTracker)changeTracker)._persistence_getPropertyChangeListener(); |
| if (listener != null) { |
| listener.ignoreEvents(); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is used to enable changetracking temporarily |
| */ |
| @Override |
| public void enableEventProcessing(Object changeTracker) { |
| ObjectChangeListener listener = (ObjectChangeListener)((ChangeTracker)changeTracker)._persistence_getPropertyChangeListener(); |
| if (listener != null) { |
| listener.processEvents(); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if the Object should be compared, false otherwise. In ObjectChangeTrackingPolicy or |
| * AttributeChangeTracking Policy this method will return true if the object is new, if the object |
| * is in the OptimisticReadLock list or if the listener.hasChanges() returns true. |
| * @param object the object that will be compared |
| * @param unitOfWork the active unitOfWork |
| * @param descriptor the descriptor for the current object |
| */ |
| @Override |
| public boolean shouldCompareExistingObjectForChange(Object object, UnitOfWorkImpl unitOfWork, ClassDescriptor descriptor) { |
| //PERF: Breakdown the logic to have the most likely scenario checked first |
| ObjectChangeListener listener = (ObjectChangeListener)((ChangeTracker)object)._persistence_getPropertyChangeListener(); |
| if ((listener != null) && listener.hasChanges()) { |
| return true; |
| } |
| Boolean optimisticRead = null; |
| if (unitOfWork.hasOptimisticReadLockObjects()) { |
| optimisticRead = (Boolean)unitOfWork.getOptimisticReadLockObjects().get(object); |
| // Need to always compare/build change set for new objects and those that are being forced to |
| // updated (opt. read lock and forceUpdate) |
| if (optimisticRead != null) { |
| return true; |
| } |
| } |
| if ((descriptor.getCMPPolicy() != null) && descriptor.getCMPPolicy().getForceUpdate()) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * INTERNAL: |
| * This may cause a property change event to be raised to a listner in the case that a listener exists. |
| * If there is no listener then this call is a no-op |
| */ |
| @Override |
| public void raiseInternalPropertyChangeEvent(Object source, String propertyName, Object oldValue, Object newValue) { |
| ObjectChangeListener listener = (ObjectChangeListener)((ChangeTracker)source)._persistence_getPropertyChangeListener(); |
| if (listener != null) { |
| listener.internalPropertyChange(new PropertyChangeEvent(source, propertyName, oldValue, newValue)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Assign ChangeListener to an aggregate object |
| */ |
| @Override |
| public void setAggregateChangeListener(Object parent, Object aggregate, UnitOfWorkImpl uow, ClassDescriptor descriptor, String mappingAttribute) { |
| ((ChangeTracker)aggregate)._persistence_setPropertyChangeListener(new AggregateObjectChangeListener((ObjectChangeListener)((ChangeTracker)parent)._persistence_getPropertyChangeListener(), mappingAttribute)); |
| } |
| |
| /** |
| * INTERNAL: |
| * Assign ObjectChangeListener to PropertyChangeListener |
| */ |
| @Override |
| public PropertyChangeListener setChangeListener(Object clone, UnitOfWorkImpl uow, ClassDescriptor descriptor) { |
| ObjectChangeListener listener = new ObjectChangeListener(); |
| ((ChangeTracker)clone)._persistence_setPropertyChangeListener(listener); |
| return listener; |
| } |
| |
| /** |
| * INTERNAL: |
| * Clear the changes in the ObjectChangeListener |
| */ |
| @Override |
| public void clearChanges(Object clone, UnitOfWorkImpl uow, ClassDescriptor descriptor, boolean forRefresh) { |
| ObjectChangeListener listener = (ObjectChangeListener)((ChangeTracker)clone)._persistence_getPropertyChangeListener(); |
| if (listener != null) { |
| listener.clearChanges(forRefresh); |
| } else { |
| listener = (ObjectChangeListener)setChangeListener(clone, uow, descriptor); |
| } |
| ObjectBuilder builder = descriptor.getObjectBuilder(); |
| // Only relationship mappings need to be reset. |
| if (!builder.isSimple()) { |
| dissableEventProcessing(clone); |
| // Must also ensure the listener has been set on collections and aggregates. |
| FetchGroupManager fetchGroupManager = descriptor.getFetchGroupManager(); |
| boolean isPartialObject = (fetchGroupManager != null) && fetchGroupManager.isPartialObject(clone); |
| List<DatabaseMapping> mappings = builder.getRelationshipMappings(); |
| int size = mappings.size(); |
| // Only cascade fetched mappings. |
| for (int index = 0; index < size; index++) { |
| DatabaseMapping mapping = mappings.get(index); |
| if (!isPartialObject || fetchGroupManager.isAttributeFetched(clone, mapping.getAttributeName())) { |
| mapping.setChangeListener(clone, listener, uow); |
| } |
| } |
| enableEventProcessing(clone); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * initialize the Policy |
| */ |
| @Override |
| public void initialize(AbstractSession session, ClassDescriptor descriptor) { |
| //3934266 If changePolicy is ObjectChangeTrackingPolicy or AttributeChangeTrackingPolicy, the class represented |
| //by the descriptor must implement ChangeTracker interface. Otherwise throw an exception. |
| Class javaClass = descriptor.getJavaClass(); |
| if (!ChangeTracker.class.isAssignableFrom(javaClass)) { |
| session.getIntegrityChecker().handleError(DescriptorException.needToImplementChangeTracker(descriptor)); |
| } |
| } |
| |
| /** |
| * Used to track instances of the change policies without doing an instance of check |
| */ |
| @Override |
| public boolean isDeferredChangeDetectionPolicy(){ |
| return false; |
| } |
| |
| /** |
| * Used to track instances of the change policies without doing an instance of check |
| */ |
| @Override |
| public boolean isObjectChangeTrackingPolicy() { |
| return true; |
| } |
| } |