| /* |
| * 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; |
| |
| import java.util.*; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.DatabaseMapping.WriteType; |
| import org.eclipse.persistence.core.descriptors.CoreDescriptorEvent; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.exceptions.DescriptorException; |
| import org.eclipse.persistence.queries.*; |
| import org.eclipse.persistence.sessions.DataRecord; |
| import org.eclipse.persistence.internal.sessions.*; |
| |
| /** |
| * <p><b>Purpose</b>: Encapsulate the information provided with descriptor events. |
| * This is used as the argument to any event raised by the descriptor. |
| * Events can be registered for through two methods, the first is by providing a method |
| * to be called on the object that a particular operation is being performed on. |
| * The second is by registering a manager object to be notified when any event occurs |
| * for that descriptor. The second method is more similar to the java beans event model |
| * but requires the registered object to implement the DescriptorEventListener interface. |
| * |
| * @see DescriptorEventManager |
| * @see DescriptorEventListener |
| */ |
| public class DescriptorEvent extends EventObject implements CoreDescriptorEvent { |
| |
| /** |
| * The code of the descriptor event being raised. |
| * This is an integer constant value from DescriptorEventManager. |
| */ |
| protected int eventCode; |
| |
| /** The query causing the event. */ |
| protected DatabaseQuery query; |
| |
| /** Optionally a database row may be provided on some events, (such as aboutToUpdate). */ |
| protected DataRecord dataRecord; |
| protected ClassDescriptor descriptor; |
| |
| /** |
| * The source object represents the object the event is being raised on, |
| * some events also require a second object, for example the original object in a postClone. |
| */ |
| protected Object originalObject; |
| |
| /** For the post merge event it is possible that there has been a change set generated. |
| * This attribute will store the changeSet for the object just merged |
| */ |
| protected ObjectChangeSet changeSet; |
| |
| /** The session in which the event is raised. */ |
| protected AbstractSession session; |
| |
| /** Event names for toString() */ |
| protected static String[] eventNames; |
| |
| /** Initialize the values */ |
| static { |
| eventNames = new String[DescriptorEventManager.NumberOfEvents]; |
| |
| eventNames[DescriptorEventManager.PreWriteEvent] = "PreWriteEvent"; |
| eventNames[DescriptorEventManager.PostWriteEvent] = "PostWriteEvent"; |
| eventNames[DescriptorEventManager.PreDeleteEvent] = "PostDeleteEvent"; |
| eventNames[DescriptorEventManager.PostDeleteEvent] = "PostDeleteEvent"; |
| eventNames[DescriptorEventManager.PreInsertEvent] = "PreInsertEvent"; |
| eventNames[DescriptorEventManager.PostInsertEvent] = "PostInsertEvent"; |
| eventNames[DescriptorEventManager.PreUpdateEvent] = "PreUpdateEvent"; |
| eventNames[DescriptorEventManager.PostUpdateEvent] = "PostUpdateEvent"; |
| eventNames[DescriptorEventManager.PostBuildEvent] = "PostBuildEvent"; |
| eventNames[DescriptorEventManager.PostRefreshEvent] = "PostRefreshEvent"; |
| eventNames[DescriptorEventManager.PostCloneEvent] = "PostCloneEvent"; |
| eventNames[DescriptorEventManager.PostMergeEvent] = "PostMergeEvent"; |
| eventNames[DescriptorEventManager.AboutToInsertEvent] = "AboutToInsertEvent"; |
| eventNames[DescriptorEventManager.AboutToUpdateEvent] = "AboutToUpdateEvent"; |
| } |
| |
| /** |
| * PUBLIC: |
| * Most events are trigger from queries, so this is a helper method. |
| */ |
| public DescriptorEvent(int eventCode, ObjectLevelModifyQuery query) { |
| this(query.getObject()); |
| this.query = query; |
| this.eventCode = eventCode; |
| this.session = query.getSession(); |
| this.descriptor = query.getDescriptor(); |
| } |
| |
| /** |
| * PUBLIC: |
| * All events require a source object. |
| */ |
| public DescriptorEvent(Object sourceObject) { |
| super(sourceObject); |
| } |
| |
| /** |
| * PUBLIC: |
| * Re-populate the database row with the values from the source object based upon the |
| * attribute's mapping. Provided as a helper method for modifying the row during event |
| * handling. |
| */ |
| public void applyAttributeValuesIntoRow(String attributeName) { |
| ClassDescriptor descriptor = getSession().getDescriptor(getSource()); |
| DatabaseMapping mapping = descriptor.getObjectBuilder().getMappingForAttributeName(attributeName); |
| |
| if (mapping == null) { |
| throw ValidationException.missingMappingForAttribute(descriptor, attributeName, this.toString()); |
| } |
| if (getRecord() != null) { |
| mapping.writeFromObjectIntoRow(getSource(), (AbstractRecord)getRecord(), getSession(), WriteType.UNDEFINED); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns the Object changeSet if available |
| */ |
| public ObjectChangeSet getChangeSet() { |
| return changeSet; |
| } |
| |
| /** |
| * PUBLIC: |
| * The source descriptor of the event. |
| */ |
| public ClassDescriptor getDescriptor() { |
| return descriptor; |
| } |
| |
| /** |
| * PUBLIC: |
| * The source descriptor of the event. |
| */ |
| public ClassDescriptor getClassDescriptor() { |
| return descriptor; |
| } |
| |
| /** |
| * PUBLIC: |
| * The code of the descriptor event being raised. |
| * This is an integer constant value from DescriptorEventManager. |
| */ |
| public int getEventCode() { |
| return eventCode; |
| } |
| |
| /** |
| * PUBLIC: |
| * Synanym for source. |
| */ |
| public Object getObject() { |
| return getSource(); |
| } |
| |
| /** |
| * PUBLIC: |
| * The source object represents the object the event is being raised on, |
| * some events also require a second object, for example the original object in a postClone. |
| * |
| * @see EventObject#getSource() |
| */ |
| public Object getOriginalObject() { |
| // Compute the original for unit of work writes. |
| if ((originalObject == null) && getSession().isUnitOfWork() && (getQuery() != null) && (getQuery().isObjectLevelModifyQuery())) { |
| setOriginalObject(((UnitOfWorkImpl)getSession()).getOriginalVersionOfObject(getSource())); |
| } |
| return originalObject; |
| } |
| |
| /** |
| * PUBLIC: |
| * The query causing the event. |
| */ |
| public DatabaseQuery getQuery() { |
| return query; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the record that is associated with some events, |
| * such as postBuild, and aboutToUpdate. |
| */ |
| public DataRecord getRecord() { |
| return dataRecord; |
| } |
| |
| /** |
| * PUBLIC: |
| * The session in which the event is raised. |
| */ |
| public AbstractSession getSession() { |
| return session; |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets the Change set in the event if the change Set is available |
| */ |
| public void setChangeSet(ObjectChangeSet newChangeSet) { |
| changeSet = newChangeSet; |
| } |
| |
| /** |
| * INTERNAL: |
| * The source descriptor of the event. |
| */ |
| public void setDescriptor(ClassDescriptor descriptor) { |
| this.descriptor = descriptor; |
| } |
| |
| /** |
| * INTERNAL: |
| * The code of the descriptor event being raised. |
| * This is an integer constant value from DescriptorEventManager. |
| */ |
| public void setEventCode(int eventCode) { |
| this.eventCode = eventCode; |
| } |
| |
| /** |
| * INTERNAL: |
| * The source object represents the object the event is being raised on, |
| * some events also require a second object, for example the original object in a postClone. |
| */ |
| public void setOriginalObject(Object originalObject) { |
| this.originalObject = originalObject; |
| } |
| |
| /** |
| * INTERNAL: |
| * The query causing the event. |
| */ |
| public void setQuery(DatabaseQuery query) { |
| this.query = query; |
| } |
| |
| /** |
| * INTERNAL: |
| * Optionally a database row may be provided on some events, (such as aboutToUpdate). |
| */ |
| public void setRecord(DataRecord dataRecord) { |
| this.dataRecord = dataRecord; |
| } |
| |
| /** |
| * INTERNAL: |
| * The session in which the event is raised. |
| */ |
| public void setSession(AbstractSession session) { |
| this.session = session; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public String toString() { |
| String eventName = "UnkownEvent"; |
| |
| if ((getEventCode() >= 0) && (getEventCode() < DescriptorEventManager.NumberOfEvents)) { |
| eventName = eventNames[getEventCode()]; |
| } |
| |
| return eventName + "(" + getSource().getClass() + ")"; |
| } |
| |
| /** |
| * ADVANCED: |
| * Use this method when updating object attribute values, with unmapped objects Integer, String or others. in events to ensure that all |
| * required objects are updated. EclipseLink will automatically update all objects and changesets |
| * involved. EclipseLink will update the field, in the row, to have the new value for the field |
| * that this mapping maps to. |
| */ |
| public void updateAttributeWithObject(String attributeName, Object value) { |
| DatabaseMapping mapping = this.query.getDescriptor().getObjectBuilder().getMappingForAttributeName(attributeName); |
| if (mapping == null) { |
| throw DescriptorException.mappingForAttributeIsMissing(attributeName, getDescriptor()); |
| } |
| |
| Object clone = this.getObject(); |
| Object cloneValue = value; |
| Object original = null; |
| |
| //only set the original object if we need to update it, i.e. before the merge takes place |
| if ((this.eventCode == DescriptorEventManager.PostCloneEvent) || (this.eventCode == DescriptorEventManager.PostMergeEvent)) { |
| original = this.getOriginalObject(); |
| } |
| Object originalValue = value; |
| ObjectChangeSet eventChangeSet = this.getChangeSet(); |
| // TODO: valueForChangeSet is never used, but seems it should be? The compareForChange is only valid with a backup clone. |
| Object valueForChangeSet = value; |
| |
| if ((this.query != null) && this.query.isObjectLevelModifyQuery()) { |
| clone = ((ObjectLevelModifyQuery)this.query).getObject(); |
| eventChangeSet = ((ObjectLevelModifyQuery)this.query).getObjectChangeSet(); |
| } |
| ClassDescriptor descriptor = getSession().getDescriptor(value.getClass()); |
| |
| if (descriptor != null) { |
| //There is a descriptor for the value being passed in so we must be careful |
| // to convert the value before assigning it. |
| if (eventChangeSet != null) { |
| valueForChangeSet = descriptor.getObjectBuilder().createObjectChangeSet(value, (UnitOfWorkChangeSet)eventChangeSet.getUOWChangeSet(), getSession()); |
| } |
| if (original != null) { |
| // must be a unitOfWork because only the postMerge, and postClone events set this attribute |
| originalValue = ((UnitOfWorkImpl)getSession()).getOriginalVersionOfObject(value); |
| } |
| } |
| if (clone != null) { |
| mapping.setRealAttributeValueInObject(clone, cloneValue); |
| } |
| if (original != null) { |
| mapping.setRealAttributeValueInObject(original, originalValue); |
| } |
| if (getRecord() != null) { |
| AbstractRecord tempRow = getDescriptor().getObjectBuilder().createRecord(getSession()); |
| |
| // pass in temp Row because most mappings use row.add() not row.put() for |
| // perf reasons. We are using writeFromObjectIntoRow in order to support |
| // a large number of types. |
| mapping.writeFromObjectIntoRow(clone, tempRow, getSession(), WriteType.UNDEFINED); |
| ((AbstractRecord)getRecord()).mergeFrom(tempRow); |
| } |
| if(eventChangeSet != null && (!eventChangeSet.isNew() || (query.getDescriptor() != null && query.getDescriptor().shouldUseFullChangeSetsForNewObjects()))) { |
| eventChangeSet.removeChange(attributeName); |
| // TODO: Can't see this working with attribute change tracking with no backup clone. |
| eventChangeSet.addChange(mapping.compareForChange(clone, ((UnitOfWorkImpl)getSession()).getBackupClone(clone, getDescriptor()), eventChangeSet, getSession())); |
| eventChangeSet.setShouldRecalculateAfterUpdateEvent(false); |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * Use this method when updating object attribute values, with unmapped objects Integer, String or others. in events to ensure that all |
| * required objects are updated. EclipseLink will automatically update all objects and changesets |
| * involved. EclipseLink will update the field, in the row, to have the new value for the field |
| * that this mapping maps to. If the attribute being updated is within an aggregate then pass the updated aggregate |
| * and the attribute of the aggregate mapping into this method. |
| */ |
| public void updateAttributeAddObjectToCollection(String attributeName, Object mapKey, Object value) { |
| DatabaseMapping mapping = this.query.getDescriptor().getObjectBuilder().getMappingForAttributeName(attributeName); |
| if (mapping == null) { |
| throw DescriptorException.mappingForAttributeIsMissing(attributeName, getDescriptor()); |
| } |
| |
| Object clone = this.getObject(); |
| Object cloneValue = value; |
| Object original = null; |
| |
| //only set the original object if we need to update it, ie before the merge takes place |
| if ((this.eventCode == DescriptorEventManager.PostCloneEvent) || (this.eventCode == DescriptorEventManager.PostMergeEvent)) { |
| original = this.getOriginalObject(); |
| } |
| Object originalValue = value; |
| ObjectChangeSet eventChangeSet = this.getChangeSet(); |
| Object valueForChangeSet = value; |
| |
| if ((this.query != null) && this.query.isObjectLevelModifyQuery()) { |
| clone = ((ObjectLevelModifyQuery)this.query).getObject(); |
| eventChangeSet = ((ObjectLevelModifyQuery)this.query).getObjectChangeSet(); |
| } |
| ClassDescriptor descriptor = getSession().getDescriptor(value.getClass()); |
| |
| if (descriptor != null) { |
| //There is a descriptor for the value being passed in so we must be carefull |
| // to convert the value before assigning it. |
| if (eventChangeSet != null) { |
| valueForChangeSet = descriptor.getObjectBuilder().createObjectChangeSet(value, (UnitOfWorkChangeSet)eventChangeSet.getUOWChangeSet(), getSession()); |
| } |
| if (original != null) { |
| // must be a unitOfWork because only the postMerge, and postClone events set this attribute |
| originalValue = ((UnitOfWorkImpl)getSession()).getOriginalVersionOfObject(value); |
| } |
| } |
| |
| if (clone != null) { |
| Object collection = mapping.getRealCollectionAttributeValueFromObject(clone, getSession()); |
| mapping.getContainerPolicy().addInto(mapKey, cloneValue, collection, getSession()); |
| } |
| if (original != null) { |
| Object collection = mapping.getRealCollectionAttributeValueFromObject(original, getSession()); |
| mapping.getContainerPolicy().addInto(mapKey, originalValue, collection, getSession()); |
| } |
| if (getRecord() != null) { |
| AbstractRecord tempRow = getDescriptor().getObjectBuilder().createRecord(getSession()); |
| |
| // pass in temp Row because most mappings use row.add() not row.put() for |
| // perf reasons. We are using writeFromObjectIntoRow in order to support |
| // a large number of types. |
| mapping.writeFromObjectIntoRow(clone, tempRow, getSession(), WriteType.UNDEFINED); |
| ((AbstractRecord)getRecord()).mergeFrom(tempRow); |
| } |
| if (eventChangeSet != null) { |
| mapping.simpleAddToCollectionChangeRecord(mapKey, valueForChangeSet, eventChangeSet, getSession()); |
| eventChangeSet.setShouldRecalculateAfterUpdateEvent(false); |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * Use this method when updating object attribute values, with unmapped objects Integer, String or others. in events to ensure that all |
| * required objects are updated. EclipseLink will automatically update all objects and changesets |
| * involved. EclipseLink will update the field, in the row, to have the new value for the field |
| * that this mapping maps to. |
| */ |
| public void updateAttributeRemoveObjectFromCollection(String attributeName, Object mapKey, Object value) { |
| DatabaseMapping mapping = this.query.getDescriptor().getObjectBuilder().getMappingForAttributeName(attributeName); |
| if (mapping == null) { |
| throw DescriptorException.mappingForAttributeIsMissing(attributeName, getDescriptor()); |
| } |
| |
| Object clone = this.getObject(); |
| Object cloneValue = value; |
| Object original = null; |
| |
| //only set the original object if we need to update it, ie before the merge takes place |
| if ((this.eventCode == DescriptorEventManager.PostCloneEvent) || (this.eventCode == DescriptorEventManager.PostMergeEvent)) { |
| original = this.getOriginalObject(); |
| } |
| Object originalValue = value; |
| ObjectChangeSet eventChangeSet = this.getChangeSet(); |
| Object valueForChangeSet = value; |
| |
| if ((this.query != null) && this.query.isObjectLevelModifyQuery()) { |
| clone = ((ObjectLevelModifyQuery)this.query).getObject(); |
| eventChangeSet = ((ObjectLevelModifyQuery)this.query).getObjectChangeSet(); |
| } |
| ClassDescriptor descriptor = getSession().getDescriptor(value.getClass()); |
| |
| if (descriptor != null) { |
| //There is a descriptor for the value being passed in so we must be carefull |
| // to convert the value before assigning it. |
| if (eventChangeSet != null) { |
| valueForChangeSet = descriptor.getObjectBuilder().createObjectChangeSet(value, (UnitOfWorkChangeSet)eventChangeSet.getUOWChangeSet(), getSession()); |
| } |
| if (original != null) { |
| // must be a unitOfWork because only the postMerge, and postClone events set this attribute |
| originalValue = ((UnitOfWorkImpl)getSession()).getOriginalVersionOfObject(value); |
| } |
| } |
| if (clone != null) { |
| Object collection = mapping.getRealCollectionAttributeValueFromObject(clone, getSession()); |
| mapping.getContainerPolicy().removeFrom(mapKey, cloneValue, collection, getSession()); |
| } |
| if (original != null) { |
| Object collection = mapping.getRealCollectionAttributeValueFromObject(original, getSession()); |
| mapping.getContainerPolicy().removeFrom(mapKey, originalValue, collection, getSession()); |
| } |
| if (getRecord() != null) { |
| AbstractRecord tempRow = getDescriptor().getObjectBuilder().createRecord(getSession()); |
| |
| // pass in temp Row because most mappings use row.add() not row.put() for |
| // perf reasons. We are using writeFromObjectIntoRow in order to support |
| // a large number of types. |
| mapping.writeFromObjectIntoRow(clone, tempRow, getSession(), WriteType.UNDEFINED); |
| ((AbstractRecord)getRecord()).mergeFrom(tempRow); |
| } |
| if (eventChangeSet != null) { |
| mapping.simpleRemoveFromCollectionChangeRecord(mapKey, valueForChangeSet, eventChangeSet, getSession()); |
| eventChangeSet.setShouldRecalculateAfterUpdateEvent(false); |
| } |
| } |
| } |