RecordChangeEvent.java
/*
** Module : RecordChangeEvent.java
** Abstract : Event sent with DMO state change notifications
**
** Copyright (c) 2004-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------------------------Description----------------------------------
** 001 ECF 20060719 @28120 Created initial version. Defines the event
** sent with DMO state change notifications.
** 002 ECF 20060912 @29483 Fixed defect in constructor. Unsafe to make a
** deep copy of snapshot DMO when DMO has been
** deleted. If DMO has collections, Hibernate
** will not be able to initialize them after
** record has been deleted.
** 003 ECF 20061207 @31615 Support insert event type. Added isInsert()
** method.
** 004 ECF 20070707 @34416 Optimized constructor. It is not necessary to
** make a deep copy of the snapshot, since it is
** already a copy of the original DMO.
** 005 ECF 20070926 @35245 Added isUpdate() method. Reports whether
** event signifies a DMO update.
** 006 ECF 20071130 @36127 Integrated generics.
** 007 ECF 20080814 @39551 Added explicit type field and Type enum.
** Previously, we divined the type of event from
** its state, but this proved unreliable for
** subclasses.
** 008 ECF 20090107 @41013 Added support for bulk delete events. Full
** and partial deletes can be differentiated
** from one another.
** 009 ECF 20200906 New ORM implementation.
** 010 TJD 20240123 Java 17 compatibility updates
** 011 CA 20250305 Added state-changed 'refresh buffers' event, to force reload of all buffers in
** current context, for the specified DMO.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.persist.event;
import java.io.*;
import java.util.*;
import com.goldencode.p2j.persist.*;
import com.goldencode.p2j.persist.Record;
/**
* The event sent with DMO state change notifications. Encapsulates
* information about the event's source, the original DMO state, the current
* DMO state, and which properties were modified. Depending upon the type of
* event (update, insert, or delete), some of these data may be missing.
*/
public class RecordChangeEvent
implements Serializable
{
/** Event types */
public static enum Type
implements Serializable
{
/** Record update event */
UPDATE,
/** Record insert event */
INSERT,
/** Record delete event */
DELETE,
/** Record bulk delete event; all records deleted */
FULL_BULK_DELETE,
/** Record bulk delete event; selected records deleted */
RESTRICTED_BULK_DELETE,
/**
* Event raised after a direct JDBC access has been made on a table, to force refresh all buffers in
* current session.
*/
BUFFER_REFRESH;
};
/** Event type */
private final Type type;
/** Record buffer which generated this event */
private final transient RecordBuffer source;
/** Changed record, reflects current state of DMO */
private final Record dmo;
/** Snapshot of the original DMO, before any change was made */
private final Record snapshot;
/** Map of property names to extents (for extent fields) */
private final Map<String, List<Integer>> properties;
/**
* Constructor.
*
* @param type
* Event type.
* @param source
* Record buffer which originated the event.
* @param dmo
* <b>Post-modification</b> state of the affected DMO (in the
* case where the modification is the deletion of the DMO from
* the database, this will be the object instance which was
* deleted).
* @param snapshot
* A deep copy of the DMO which reflects the state of the DMO as
* it was first set into the record buffer. For a newly created
* record, this will contain the primary key assigned to the
* record. All other properties will be set to their default
* values.
* @param properties
* A map whose keys are the names of those properties which have
* been modified and whose values are the indexes at which those
* values are stored (indexed properties only). For a simple
* property, the matching value will be <code>null</code>.
*/
public RecordChangeEvent(Type type,
RecordBuffer source,
Record dmo,
Record snapshot,
Map<String, List<Integer>> properties)
{
this.type = type;
this.source = source;
this.dmo = dmo;
this.snapshot = snapshot;
this.properties = (properties != null ? Collections.unmodifiableMap(properties) : null);
}
/**
* Get the record buffer which is the source of the change event.
* <p>
* Note: this will be <code>null</code> if this event has been serialized
* and deserialized; RecordBuffer objects cannot travel.
*
* @return Source record buffer.
*/
public RecordBuffer getSource()
{
return source;
}
/**
* Get the DMO type associated with this event. This corresponds with the
* DMO implementation class' fully qualified name.
*
* @return DMO entity name.
*/
public String getEntity()
{
return source.getEntityName();
}
/**
* Get the record which represents the <b>post-modification</b> state of
* the affected DMO (in the case where the modification is the deletion of
* the DMO from the database, this will be the object instance which was
* deleted).
*
* @return Modified DMO.
*/
public Record getDMO()
{
return dmo;
}
/**
* Get a deep copy snapshot of the DMO which reflects the state of the DMO
* as it was first set into the record buffer. For a newly created
* record, this will contain the primary key assigned to the record. All
* other properties will be set to their default values.
*
* @return Snapshot of the unmodified DMO.
*/
public Record getSnapshot()
{
return snapshot;
}
/**
* Get a map whose keys are the names of those properties which have been modified and whose values are
* lists of the indexes at which those values are stored (indexed properties only). For a simple property,
* the matching value will be {@code null}. If the change represented by the event was a deletion of the
* record, the map will be empty.
*
* @return An unmodifiable map as described above.
*
* @see #isDelete
*/
public Map<String, List<Integer>> getProperties()
{
if (properties == null)
{
return Collections.emptyMap();
}
return properties;
}
/**
* Does this event signify a record insert?
*
* @return <code>true</code> if event represents record insert, else
* <code>false</code>.
*/
public boolean isInsert()
{
return (type == Type.INSERT);
}
/**
* Does this event signify a record delete?
*
* @return <code>true</code> if event represents record delete, else
* <code>false</code>.
*/
public boolean isDelete()
{
return (type == Type.DELETE);
}
/**
* Does this event signify a bulk record delete?
*
* @return <code>true</code> if event represents bulk record delete, else
* <code>false</code>.
*/
public boolean isBulkDelete()
{
return (type == Type.FULL_BULK_DELETE || type == Type.RESTRICTED_BULK_DELETE);
}
/**
* Does this event signify a <b>full</b> bulk record delete (all records in
* a temp table with a given multiplex ID)?
*
* @return <code>true</code> if event represents full bulk record delete,
* else <code>false</code>.
*/
public boolean isFullBulkDelete()
{
return (type == Type.FULL_BULK_DELETE);
}
/**
* Does this event signify a modification to one or more of a record's
* properties?
*
* @return <code>true</code> if event represents record update, else
* <code>false</code>.
*/
public boolean isUpdate()
{
return (type == Type.UPDATE);
}
/**
* Does this event signify a force buffer refresh, as JDBC direct access was used on the table?
*
* @return <code>true</code> if event represents buffer refresh, else <code>false</code>.
*/
public boolean isBufferRefresh()
{
return (type == Type.BUFFER_REFRESH);
}
/**
* Report whether this change impacts only extent properties, or simple
* properties as well. This may be interesting to listeners who do not
* care about updates to indexed properties.
*
* @return <code>true</code> if change impacts only extent properties;
* <code>false</code> if change impacts any simple property in
* the affected DMO.
*/
public boolean isExtentFieldChangeOnly()
{
Iterator<?> iter = getProperties().values().iterator();
while (iter.hasNext())
{
if (iter.next() == null)
{
return false;
}
}
return true;
}
}