blob: bb6451781da2f46903b6794bd23ad0d42ba916e8 [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
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;
}
}