Skip to main content

Defining Callback Methods

Callback methods are called by system methods to allow additional user-supplied processing. Callback methods are identifiable by having names that begin with %On or On, typically followed by the name of the method that invokes them.

If a system method has an implemented callback method, then when the system method runs, that method invokes the callback method. For example, %Delete() invokes %OnDelete(), if %OnDelete() is implemented.

Important:

Do not execute callback methods directly.

Callback Methods
Callback Name Implemented for Method Type Private Method?
%OnAddToSaveSet() %RegisteredObjectOpens in a new tab Instance Yes
%OnAfterBuildIndices() %PersistentOpens in a new tab Class Yes
%OnAfterDelete() %PersistentOpens in a new tab Class Yes
%OnAfterPurgeIndices() %PersistentOpens in a new tab Class Yes
%OnAfterSave() %PersistentOpens in a new tab Instance Yes
%OnBeforeBuildIndices() %PersistentOpens in a new tab Class Yes
%OnBeforePurgeIndices() %PersistentOpens in a new tab Class Yes
%OnBeforeSave() %PersistentOpens in a new tab Instance Yes
%OnClose() %RegisteredObjectOpens in a new tab Instance Yes
%OnConstructClone() %RegisteredObjectOpens in a new tab Instance Yes
%OnDelete() %PersistentOpens in a new tab Class Yes
%OnDeleteFinally() %PersistentOpens in a new tab Class No
%OnNew() %RegisteredObjectOpens in a new tab Instance Yes
%OnOpen() %PersistentOpens in a new tab, %SerialObjectOpens in a new tab Instance Yes
%OnOpenFinally() %PersistentOpens in a new tab Instance No
%OnReload %PersistentOpens in a new tab Instance Yes
%OnRollBack %PersistentOpens in a new tab Instance Yes
%OnSaveFinally() %PersistentOpens in a new tab Instance No
%OnValidateObject() %RegisteredObjectOpens in a new tab Instance Yes
%OnDetermineClass() %Storage.Persistent Class No
Note:

For all callbacks that are private methods, documentation for them is only visible in the Class Reference if the Private check box in the upper-right corner of the Class Reference is selected.

Callbacks and Triggers

For an application that uses both SQL and object access, if you implement a trigger, it is generally desirable to call the same logic at an equivalent point in object access. For example, if you insert an audit record when a row is deleted, you should probably also insert an audit record if an object is deleted.

If a trigger is defined with Foreach = row/object, then the trigger is also called at specific points during object access. See Triggers and Transactions.

If, however, you cannot create such triggers, and if you want the SQL and object behavior to be synchronized in the sense described previously, then it is necessary to implement one or more callbacks. In these implementations, use logic equivalent to that used in the trigger definitions. Note that the following callback methods have functionality equivalent to that of SQL triggers:

  • %OnBeforeSave() — BEFORE INSERT, BEFORE UPDATE

  • %OnAfterSave() — AFTER INSERT, AFTER UPDATE

  • %OnDelete() — BEFORE DELETE

For more information on triggers, see the Using Triggers or the CREATE TRIGGER page in the InterSystems SQL Reference.

%OnAddToSaveSet()

This instance method is called whenever the current object is being added to a SaveSet by %AddToSaveSet(). %AddToSaveSet() can be called by:

If %OnAddToSaveSet() modifies another object, then it is the responsibility of %OnAddToSaveSet() to invoke %AddToSaveSet() on that modified object. When calling %AddToSaveSet() from %OnAddToSaveSet(), pass the depth as the first argument and 1 (literal one) as the second argument.

When you invoke %Save() on an object, called, for example, MyPerson, the system generates a list of objects that MyPerson references. A SaveSet is the list of objects consisting of the object to be saved and all the objects that it references. In the example, the SaveSet might include referenced objects MySpouse, MyDoctor, and so on. For a fuller discussion, see Saving Objects.

The signature of %OnAddToSaveSet() is:

Method %OnAddToSaveSet(depth As %Integer,
                       insert As %Integer,
                       callcount As %Integer)
                        As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

where:

depth

An integer value passed in from %AddToSaveSet() that represents the internal state of SaveSet construction. If you use %OnAddToSaveSet() to add any other objects to the SaveSet, pass this value to %AddToSaveSet() without change.

insert

A flag indicating if the object being saved is being inserted into the extent (1) or that it is already part of the extent (0).

callcount

The number of times that %OnAddToSaveSet has been called for this object. Due to the networked nature of object references, it is possible that %AddToSaveSet can be invoked on the same object multiple times.

The method returns a %StatusOpens in a new tab code, where a failure status causes the save to fail and the transaction to be rolled back.

You can update objects, create new objects, delete objects and ask objects to include themselves in the current SaveSet by calling %AddToSaveSet(). If you modify the current instance or any of its descendants, you must let the system know that you have done this; to do so, call %AddToSaveSet() for the modified instance(s) and specify the Refresh argument as 1.

None of the modification restrictions imposed on %OnAfterSave(), %OnBeforeSave(), or %OnValidateObject() are in place for %OnAddToSaveSet().

If you delete an object using %OnAddToSaveSet(), be sure to call %RemoveFromSaveSet() to clean up any dangling references to it.

This method can be overridden in any subclass of %Library.RegisteredObjectOpens in a new tab.

%OnAfterBuildIndices()

This class method is called by the %BuildIndices() method after that method builds the indexes and executes $SortEnd and just before the method releases the extent lock (if one had been requested).

Its signature is:

ClassMethod %OnAfterBuildIndices(indexlist As %String(MAXLEN="") = "") As %Status [ Abstract, Private, ServerOnly = 1 ] 
{
    // body of method here...
}

where:

indexlist

A $List of the index names.

%OnAfterDelete()

This class method is called by the %Delete() or %DeleteId() method just after a specified object is deleted (immediately after a successful call to %DeleteData()). This method allows you to perform actions outside the scope of the object being saved, such as queuing a later notification action.

Its signature is:

ClassMethod %OnAfterDelete(oid As %ObjectIdentity) As %Status [ Private, ServerOnly = 1 ]  
{
    // body of method here...
}

where:

oid

The object being deleted.

The method returns a %StatusOpens in a new tab code, where a failure status causes %Delete() or %DeleteId() to fail and, if there is an active transaction, to roll it back. If %Delete() or %DeleteId() return an error (either its own error or one originating in %DeleteData()), then there is no call to %OnAfterDelete().

Subclasses of %Library.PersistentOpens in a new tab have the option of overriding this method.

%OnAfterPurgeIndices()

This class method is called by the %PurgeIndices() method after that method has completed all its processing.

Its signature is:

ClassMethod %OnAfterPurgeIndices(indexlist As %String(MAXLEN="") = "") As %Status [ Abstract, Private, ServerOnly = 1 ] 
{
    // body of method here...
}

where:

indexlist

A $List of the index names.

%OnAfterSave()

This instance method is called by the %Save() method just after an object is saved. This method allows you to perform actions outside the scope of the object being saved, such as queueing a later notification action. An example is a bank using the deposit in excess of a certain amount to cause it to send the customer an explanation of its deposit policies.

Its signature is:

Method %OnAfterSave(insert as %Boolean)As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

where:

insert

A flag indicating if the object being saved is being inserted into the extent (1) or that it is an update of an existing object (0).

The method returns a %StatusOpens in a new tab code, where a failure status causes %Save() to fail and ultimately roll back the transaction.

Subclasses of %Library.PersistentOpens in a new tab have the option of overriding this method.

%OnBeforeBuildIndices()

This class method is called by the %BuildIndices() method after that method acquires the extent lock (if one had been requested) and before that method starts to build indexes.

Its signature is:

ClassMethod %OnBeforeBuildIndices(ByRef indexlist As %String(MAXLEN="") = "") As %Status [ Abstract, Private, ServerOnly = 1 ]
{
    // body of method here...
}

where:

indexlist

A $List of the index names. This parameter is passed by reference. If the implementation of %OnBeforeBuildIndices() alters this value, then %BuildIndices() receives the changed value.

%OnBeforePurgeIndices()

This class method is called by the %PurgeIndices() method before that method starts work. If this method returns an error, then %PurgeIndices() will exit immediately without purging any index structures, returning the error to the caller of %PurgeIndices().

Its signature is:

ClassMethod %OnBeforePurgeIndices(ByRef indexlist As %String(MAXLEN="") = "") As %Status [ Abstract, Private, ServerOnly = 1 ] 
{
    // body of method here...
}

where:

indexlist

A $List of the index names. This parameter is passed by reference. If the implementation of %OnBeforePurgeIndices() alters this value, then %PurgeIndices() receives the changed value.

%OnBeforeSave()

This instance method is called by the %Save() method just before an object is saved. This method allows you to request user confirmation before completing an action before saving the instance to disk.

Important:

It is not valid to modify the current object in %OnBeforeSave(). If you wish to modify the object before saving it, implement the %OnAddToSaveSet() callback instead and include your logic in that method.

Its signature is:

Method %OnBeforeSave(insert as %Boolean) As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

where:

insert

A flag indicating if the object being saved is being inserted into the extent (1) or that it is already part of the extent (0).

The method returns a %StatusOpens in a new tab code, where a failure status causes the save to fail.

Subclasses of %Library.PersistentOpens in a new tab have the option of overriding this method.

%OnClose()

This instance method is called immediately before an object is destructed, thereby providing the user with an opportunity to perform operations on any ancillary items, such as releasing locks or removing temporary data structures.

Its signature is:

Method %OnClose() As %Status [ Private, ServerOnly = 1 ]
{
    // body of method here...
}

The method returns a %StatusOpens in a new tab code, where a failure status is only informational and does nothing to prevent the object from being destructed.

Subclasses of %Library.RegisteredObjectOpens in a new tab have the option of overriding this method.

%OnConstructClone()

This instance method is called by the %ConstructClone() method immediately after the structures have been allocated for the cloned object and all the data has been copied into it. The method allows you to perform any additional actions related to the cloned object, such as taking out a lock or resetting any of the property values of the clone.

Its signature is:

Method %OnConstructClone(object As %RegisteredObject,
                        deep As %Boolean,
                        ByRef cloned As %String)
                        As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

where:

object

The OREF of the object that was cloned.

deep

How “deep” the cloning process is, where 0 specifies that the clone points to the same related objects as the original; 1 causes objects related to the object being cloned to also be cloned, so that the clone gets its own set of related objects.

cloned

An argument whose use varies according to how %OnConstructClone() is being invoked. See class documentation on %Library.RegisteredObjectOpens in a new tab for details.

The method returns a %StatusOpens in a new tab code, where a failure status prevents the clone from being created.

Subclasses of %Library.RegisteredObjectOpens in a new tab have the option of overriding this method.

%OnDelete()

This class method is called by the %Delete() or %DeleteId() method just before an object is deleted. This method can be used to ensure that deleting an object does not corrupt data integrity, such as by ensuring that an object designed to contain other objects is only deleted when it is empty.

Its signature is:

ClassMethod %OnDelete(oid As %ObjectIdentity) As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

where:

oid

An object identifier for the object being deleted.

The method returns a %StatusOpens in a new tab code, where a failure status stops the deletion.

To access the properties of the object that is to be deleted, convert the OID that is passed in to an ID, and then open the object with that ID, as in the following example:

ClassMethod %OnDelete(oid As %ObjectIdentity) As %Status [ Private, ServerOnly = 1 ] 
{   
    set id = $$$oidPrimary(oid)     
    set obj = ..%OpenId(id)
    write "Deleting object with name ", obj.Name, !
    return 1 
} 

Subclasses of %Library.PersistentOpens in a new tab have the option of overriding this method.

%OnDeleteFinally()

This class method is called by the %Delete() or %DeleteId() method after all processing is complete and just before returning to the caller. If the %Delete() or %DeleteId() method started a transaction then that transaction is completed prior to invoking the callback. This method does not return any value and cannot change the result of the method that invoked it.

Its signature is:

ClassMethod %OnDeleteFinally(oid As %ObjectIdentity, status As %Status) [ ServerOnly = 1 ]
{
    // body of method here...
}

where:

oid

An object identifier for the object being deleted.

status

The status that will be reported to the user. The status cannot be modified by this callback method.

Subclasses of %Library.PersistentOpens in a new tab have the option of overriding this method.

%OnNew()

This instance method is called by the %New() method at the point when the memory for an object has been allocated and properties are initialized.

Its signature is:

Method %OnNew(initvalue As %String) As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

where:

initvalue

A string that the method uses in setting up the object, unless being overridden, as described in the next note.

Important:

The arguments for %OnNew() must match those of %New(). When customizing this method, override the arguments with whatever variables and types that you expect to receive from %New(). For example, if %New() accepts two arguments — dob for a date of birth and name for a first name and surname, the signature of %OnNew() might be:

Method %OnNew(dob as %Date = "", name as %Name = "") as %Status [ Private, ServerOnly = 1 ] 
{
 // body of method here...
}

The method returns a %StatusOpens in a new tab code, where a failure status stops the creation of the object.

For example, with a class whose instances must have a value for their Name property, the callback might be of the form:

Method %OnNew(initvalue As %String) As %Status
{
    If initvalue="" Quit $$$ERROR($$$GeneralError,"Must supply a name")
    Set ..Name=initvalue
    Quit $$$OK
}

Subclasses of %Library.RegisteredObjectOpens in a new tab have the option of overriding this method.

%OnOpen()

This instance method is called by the %Open() or %OpenId() method just before an object is opened. It allows you to verify the state of an instance compared to any relevant entities.

Its signature is:

Method %OnOpen() As %Status [ Private, ServerOnly = 1 ] {
    // body of method here...
}

The method returns a %StatusOpens in a new tab code, where a failure status stops the opening of the object.

Subclasses of %Library.PersistentOpens in a new tab and %SerialObjectOpens in a new tab have the option of overriding this method.

%OnOpenFinally()

This class method is called by the %Open() or %OpenId() method after all processing is complete and just before returning to the caller. This method does not return any value and cannot change the result of the method that invoked it.

Its signature is:

ClassMethod %OnOpenFinally(oid As %ObjectIdentity, status As %Status) [ ServerOnly = 1 ]
{
    // body of method here...
}

where:

oid

An object identifier for the object being opened.

status

The status that will be reported to the user. The status cannot be modified by this callback method.

Subclasses of %Library.PersistentOpens in a new tab have the option of overriding this method.

%OnReload()

This instance method is called by the %Reload method to provide notification that the object specified by oid was reloaded. Note that %Open calls %Reload when the object identified by oid is already in memory. If this method returns an error, the object is not opened.

Its signature is:

Method %OnReload() As %Status [ Private, ServerOnly = 1 ]
  {
    // body of method here...
  }

The method returns a %StatusOpens in a new tab code, where a failure status stops the rollback operation.

Subclasses of %Library.PersistentOpens in a new tab have the option of overriding this method.

%OnRollBack()

InterSystems IRIS® data platform calls this instance method when it rolls back an object that it had previously successfully serialized as part of a SaveSet. (See Saving Objects.)

When you invoke %Save() for a persistent object or a stream or when you invoke %GetSwizzleObject() for a serial object, the system starts a save transaction which includes all the objects in the SaveSet. If the %Save() fails (because properties do not pass validation, for example), InterSystems IRIS rolls back all objects that it had previously successfully serialized as part of a SaveSet. That is, for each of these objects, InterSystems IRIS invokes %RollBack(), which calls %OnRollBack().

InterSystems IRIS does not invoke this method for an object that has not been successfully serialized, that is, an object that is not valid.

%RollBack() restores the on-disk state of data for that object to its pre-transaction state, but does not affect the in-memory state of any properties of that object that you have set, apart from its ID assignment. (For more details, see Saving Objects.) If you want to revert in-memory changes, do so in %OnRollBack().

The signature of this method is:

Method %OnRollBack() As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

The method returns a %StatusOpens in a new tab code, where a failure status stops the rollback operation.

Subclasses of %Library.PersistentOpens in a new tab have the option of overriding this method.

%OnSaveFinally()

This class method is called by the %Save() method after all processing is complete and just before returning to the caller. If the %Save() method started a transaction then that transaction is completed prior to invoking the callback. This method does not return any value and cannot change the result of the method that invoked it.

Its signature is:

ClassMethod %OnSaveFinally(oref As %ObjectHandle, status As %Status) [ ServerOnly = 1 ]
{
    // body of method here...
}

where:

oid

An object identifier for the object being saved.

status

The status that will be reported to the user. The status cannot be modified by this callback method.

Subclasses of %Library.PersistentOpens in a new tab have the option of overriding this method.

%OnValidateObject()

This instance method is called by the %ValidateObject() method just after all validation has occurred. This allows you to perform custom validation, such as where valid values for one property vary according to the value of another property.

Its signature is:

Method %OnValidateObject() As %Status [ Private, ServerOnly = 1 ] 
{
    // body of method here...
}

The method returns a %StatusOpens in a new tab code, where a failure status causes the validation to fail.

Subclasses of %Library.RegisteredObjectOpens in a new tab have the option of overriding this method.

%OnDetermineClass()

The %OnDetermineClass() class method returns the most specific type class of that object. (For an introduction to the most specific type class, see %ClassName() and the Most Specific Type Class (MSTC).) %OnDetermineClass() is implemented by the default storage class. If you use custom storage or SQL storage, there is no default implementation for this method, but you can implement it.

Its signature is:

ClassMethod %OnDetermineClass(
        oid As %ObjectIdentity, 
        ByRef class As %String) 
    As %Status [ ServerOnly = 1 ]

where:

oid

An object identifier for the object.

class

The most specific type class of the instance identified by oid. The most specific type class of an object is the class of which the object is an instance and the object is not an instance of any subclass of that class.

The return value is a status value indicating success or failure.

Subclasses of %Library.SwizzleObjectOpens in a new tab can override this method.

Invoking %OnDetermineClass()

%OnDetermineClass() can be invoked in either of two ways:

Set status = ##class(APackage.AClass).%OnDetermineClass(myoid, .myclass)
Set status = myinstance.%OnDetermineClass(myoid, .myclass)

where myoid is the object whose most specific type class is being determined and myclass is the class identified. APackage.AClass is the class from which the method is being invoked and myinstance is the instance from which the method is being invoked.

In this case, the method is computing the most specific type class for myoid and setting myclass equal to that value. If myoid is not an instance of the current class, an error is returned.

Consider the example of using %OnDetermineClass() with Sample.Employee, which is a subclass of Sample.Person. If there is a call of the form

Set status = ##class(Sample.Employee).%OnDetermineClass(myoid, .class)

and myoid refers to an object whose most specific type class is Sample.Person, then the call returns an error.

An Example of Results of Calls to %OnDetermineClass()

Suppose there is a MyPackage.GradStudent class that extends a MyPackage.Student class that extends a MyPackage.Person class. The following shows the results of invoking %OnDetermineClass(), passing in the OID of an object whose most specific type class is MyPackage.Student:

  • ##class(MyPackage.Person).%OnDetermineClass(myOid,.myClass)

    • Return value: $$$OK

    • myClass set to: MyPackage.Student

  • ##class(MyPackage.Student).%OnDetermineClass(myOid,.myClass)

    • Return value: $$$OK

    • myClass set to: MyPackage.Student

  • ##class(MyPackage.GradStudent).%OnDetermineClass(myOid,.myClass)

    • Return value: error status

    • myClass set to: ""

See Also

FeedbackOpens in a new tab