This lesson introduces class Events where base behaviors can be tailored and custom behaviors defined. By completing this module, you will learn:
What a class event is and how it is used.
The base events that are available to all classes.
How to customize the business logic of a base event in a class.
Events are the behaviors of Classes and Aspects. A class inherits events from its base class; it can enhance or override base behaviors. Events can also take a list of arguments.
In this module, you create a new class attribute called primaryTelcom that identifies the preferred telecommunication method for reaching a given person. Then you use events to control the values that it takes on as training:Telcom instances are added and removed from the model. You also modify the isPrimary attribute that you created in Associated classes to be a calculated attribute dependent on the value of the primaryTelcom field.
Modify the business model
In this lesson you add the primaryTelcom attribute to the training:Entity class and define persistence for the new attribute.
Add the primaryTelcom attribute to the training:Entity class
To add the primaryTelcom attribute to the training:Entity class:
Open the training:Entity class and create a new attribute called primaryTelcom with the following properties:
primaryTelcom attribute properties
Attribute
Property
Value
primaryTelcom
Type
training:Telcom
Reverse
entity
In the Persistence Mapping tab, add an attribute mapping for primaryTelcom.
Set the Source Key to TrnEntity.FK_PrimaryTelcom and set the Destination Key to TrainingTelcom.PK.
Modify the isPrimary attribute of the training:Telcom class
To modify the isPrimary attribute of the training:Telcom class:
Open the training:Telcom class for editing. Go to the Attributes tab and select the isPrimary attribute.
In the Value subtab, remove the Initializer value.
Set the Value property to (= (@) (@ entity primaryTelcom)). In this expression (@) resolves to the identifier for the training:Telcom Instance; (@ entity primaryTelcom) resolves to the ID of the training:Telcom instance identified by the primaryTelcom attribute of the related Entity. If they match, then the isPrimary attribute is set to #t. Otherwise, it takes on a value of #f.
Set the Dependency value to (entity primaryTelcom). This indicates that the isPrimary attribute on a training:Telcom instance should be recalculated whenever the primaryTelcom attribute of its related training:Entity instance changes.
In the Common subtab, set the Read-Only attribute to true (selected).
In the Persistence Mapping tab, remove the attribute mapping for the isPrimary attribute. The isPrimary attribute does not need to be persisted anymore because it now behaves as a calculated attribute.
Modify the persistence model
The Update Data Source button on the Persistence Mapping tab of the classes you edited will not automatically make the changes to the persistence model for the classes you modified. You must manually modify the persistence model to support the changes you have made.
Modify persistence for the training:Entity class
To modify persistence for the training:Entity class:
In the Persistence layer, open the training:DB data source.
Select the Entity table and click the Columns tab.
Add a column with the following properties:
Column to add to the Entity table
Column Name
Type
Allocation
Allow Nulls
Precision
Case Insensitive
primaryTelcom
binary
fixed
true
16
false
In the Indexes subtab, add an index with the following properties:
Index to add to the Entity table
Name
Type
Unique
Index Columns
TrnEntity.FK_PrimaryTelcom
virtual
false
primaryTelcom
Modify persistence for the Telcom class
To modify persistence for the training:Telcom class:
Select the training:Telcom table and go to the Columns tab.
Remove the isPrimary column.
Edit the upgrade file
Add an upgrade to your upgrade file to reflect the changes in the business and persistence models. To do this, you will add steps to drop the isPrimary column from the training:Telcom table, and create the primaryTelcom column in the training:Entity table. You will additionally create a virtual index on the new primaryTelcom column.
To update the upgrade file:
In the Persistence layer on the Data Sources tab right-click the DefaultRelationalDatabase and select Generate Upgrade Steps.
Leave the default values for Upgrade and Current Data Source.
Select Old Model as the Old Data Source. You are going to use a published model as the old model.
Select Published Model JAR and then click the browse button. Select the model you published at the end of the last module and click Next.
Whenever you upgrade a database, you must increment the version number by a whole number. For example, if the version retrieved from the published JAR file is 5.39; change the Version to 6.39.
Enter the following as the Description: Dropped isPrimary column and added primaryTelcom attribute support.
Click Finish to create the upgrade steps.
A warning appears because the upgrade steps include a DropColumn. Click Proceed to continue.
Your upgrade is added to the bottom of the list of upgrades.
You must ensure that your current model's version matches the upgrade version. Click the Set current model button in the toolbar to open the Model Library.
With the current model selected, click Edit. Update the Model Version field to match the version associated with the upgrade you just created. So, if your upgrade is associated with version 5.39, then change the Model version to 6.39. This enables NexJ Studio to recognize that the model has been changed from the previous version so that your update can be applied to the supporting data stores.
Click OK. Note that the new version is now reflected in the table in the Models tab.
Click Close.
Click the Save button in the toolbar to save your work.
Publish and upgrade
Complete the model updates by carrying out the following activities.
Publish the model
Upgrade the database.
Create the events
You have now updated your model to store the primary telecommunication channel for a given entity.
If you leave your model as it is now, every time you add a training:Entity with multiple training:Telcom instances you have to explicitly identify which associated training:Telcom instance is the primary one. If you delete the primary training:Telcom instance, you have to explicitly assign a new one as primary. In this lesson, you create class events to automatically manage the primary training:Telcom instance.
Events consist of one or more actions. Every event has at least an action called main. Actions are made up of scripts or Java methods that are executed in a particular order whenever the event is invoked. Actions are executed relative to main (and to each other), can be overridden in subclasses, and can be conditionally executed.
All classes inherit a set of events from the base class Object, which has no attributes.
In this lesson, you modify two inherited events and create a new one:
You modify the inherited create event so that new training:Telcom instances automatically set their related training:Entity's primaryTelcom attribute if it is not already set.
You modify the inherited delete event.
You create a new event called setPrimaryTelcom that automatically identifies a new primary telcom when a training:Entity's primary telcom is deleted.
Add the events to the training:Telcom class
To add the three events to the training:Telcom class:
In the Business Model layer, open the training:Telcom class for editing.
In the Events tab, click the Override Base Events button and add the following events:
create()
delete ()
Click the Add button to add a new event.
Set the Name of the new event to getNextPrimary.
Info
The Visibility property for the create and delete events is set to public, while the Visibility for getNextPrimary is protected. Visibility determines how events can be accessed by client applications using a remote procedure call (RPC). Public events can be invoked by clients through RPC, protected ones are only accessible within the NexJ Server.
Add actions to the create event
To add actions to the create event.
Select the create event and click the Actions subtab.
Click the Add button and select Before. This adds a new action that runs before the main section of the create event.
Set the Name of the action to setPrimary.
Click the Script subtab, and set the Condition property to:
CODE
(null? (@ entity primaryTelcom))
Javascript
JS
this.entity.primaryTelcom == null;
This condition ensures that the script associated with the action only executes if the current training:Telcom instance's related Entity does not have a primaryTelcom (i.e. its primaryTelcom is null).
Still in the Script subtab, enter the following Scheme code in the main entry field:
CODE
; If there is no primary for this telcom's entity,
; set it to the one you are creating.
(logger'debug "setting empty primaryTelcom field on entity to " this)
((@ entity)'primaryTelcom this)
Javascript
JS
logger.debug("setting empty primaryTelcom field on entity to ", this);
this.entity.primaryTelcom = this;
Info
Note the use of logger to echo information about what the script is doing to the system log.
Save your changes.
Add actions to the delete event
Now you add actions to the delete event to check whether a deleted training:Telcom instance is identified by its related training:Entity as being the primary training:Telcom. If it is the primary training:Telcom instance, then the delete event should call the getNextPrimary event.
To add an action to the delete event:
In the Events list, select the delete event.
Click the Actions subtab.
Click the Add button and select Before. The event runs before the main action of deleting a training:Telcom instance takes place.
Set the Name of the action to clearPrimary.
Click the Script subtab, and set the Condition property to:
CODE
(= (@ entity primaryTelcom) this)
Javascript
JS
this.entity.primaryTelcom == this;
This condition ensures that the script associated with the action only executes if the current telcom's entity's primaryTelcom is the telcom that is being deleted.
Still on the Script subtab, enter the following Scheme code in the main entry field:
SCHEME
; clear the primary entity's primaryTelcom
((@ entity)'primaryTelcom '())
Javascript
JS
this.entity.primaryTelcom = null;
Add an action to the delete event that sets a new value for the primaryTelcom attribute of an Entity:
Add a second action for the delete event after clearPrimary in the Actions list.
Set the Type to before.
Set the Name to resetPrimary.
In the Script subtab, set the Condition to:
CODE
(null? (@ entity primaryTelcom))
Javascript
JS
this.entity.primaryTelcom == null
The condition confirms that the primaryTelcom attribute is null.
Enter the following script:
CODE
; reset the entity's primaryTelcom to another telcom if one exists.
((@ entity)'primaryTelcom (this'getNextPrimary))
This script only works if the associated entity has at most one training:TelephoneNumber and one training:EmailAddress associated with it. If two or more associated training:TelephoneNumber instances or training:EmailAddress instances exist, then the read-instance function returns an error. The script is a simplified example of code that you could use.
Save your work and validate your model by clicking the Validate model button in the toolbar.
A note on the script for the getNextPrimary event
The script uses read-instance to read in an instance of a class that matches its Where clause.
In this case, the Where clause is:
CODE
`(and (not (= (@) ,(@))) (= entity ,(@ entity)))
(@) A macro for the current instance.
(not (= (@) ,(@))) the instance is not equal to the current instance.
(= entity ,(@ entity)) the training:Telcom's entity attribute equals the current instance's entity attribute.
Update the unit tests
Update the training:Telcom unit test to verify that the events work as intended.
To update the unit test:
Open the training:Telcom unit test (Business Model → Unit Tests).
In the Test Cases tab, click the Add button to add a new test case.
Name the test case PrimaryDefaulting.
Enter the DescriptionTest primary defaulting.
In the test case script area, enter the following Scheme script:
CODE
(define p (training:Person'new (: firstName "Bob") (: initials "A") (: lastName "Event") (: primaryLanguage (training:LanguageEnum'get'ENGLISH))))
(define tp (training:TelcomType'new (: name "Business")))
; Part 1 - Add email and then add telephone number
; Result - Primary telcom is the email
(define email (training:EmailAddress'new (: entity p) (: type tp) (: name (tp'name)) (: address "bsled@appco.com") (: displayName "Bob Sled")))
(define tel (training:TelephoneNumber'new (: type tp) (: name (tp'name)) (: address "(416) 555-1212") (: entity p) (: extension "x123")))
(commit)
(reset-context)
(set! p (read-instance training:Person '() '(= lastName "Event") '()))
(assert-equal "bsled@appco.com" ((p'primaryTelcom)'address))
; Part 2 - Delete the primary telcom
; Result - Primary telcom is reset to phone number
((p'primaryTelcom)'delete)
(commit)
(reset-context)
(set! p (read-instance training:Person '() '(= lastName "Event") '()))
(assert-equal "(416) 555-1212" ((p'primaryTelcom)'address))
; Part 3 - Delete primary telcom
; Result - Primary Telcom remains null
((p'primaryTelcom)'delete)
(commit)
(reset-context)
(set! p (read-instance training:Person '() '(= lastName "Event") '()))
(set! tp (read-instance training:TelcomType '() '(= name "Business") '()))
(assert-null ((p'primaryTelcom)'address))
; Part 4 - Add telephone number and then add email
; Result - Primary telcom is the telephone number
; test tel added first and removed first
(set! tel (training:TelephoneNumber'new (: type tp) (: name (tp'name)) (: address "(416) 555-1212") (: entity p) (: extension "x123")))
(set! email (training:EmailAddress'new (: entity p) (: type tp) (: name (tp'name)) (: address "bsled@appco.com") (: displayName "Bob Sled")))
(commit)
(reset-context)
(set! p (read-instance training:Person '() '(= lastName "Event") '()))
(assert-equal "(416) 555-1212" ((p'primaryTelcom)'address))
; Part 5 - Delete the primary telcom
; Result - Primary telcom is reset to email
((p'primaryTelcom)'delete)
(commit)
(reset-context)
(set! p (read-instance training:Person '() '(= lastName "Event") '()))
(assert-equal "bsled@appco.com" ((p'primaryTelcom)'address))
; Part 6 - Delete primary telcom
; Result - Primary Telcom remains null
((p'primaryTelcom)'delete)
(commit)
(reset-context)
(set! p (read-instance training:Person '() '(= lastName "Event") '()))
(set! tp (read-instance training:TelcomType '() '(= name "Business") '()))
(assert-null ((p'primaryTelcom)'address))
; Part 7 - (like part 4) Add telephone number and then add email
; Result - Primary telcom is the telephone number
(set! tel (training:TelephoneNumber'new (: type tp) (: name (tp'name)) (: address "(416) 555-1212") (: entity p) (: extension "x123")))
(set! email (training:EmailAddress'new (: entity p) (: type tp) (: name (tp'name)) (: address "bsled@appco.com") (: displayName "Bob Sled")))
(commit)
(reset-context)
(set! p (read-instance training:Person '() '(= lastName "Event") '()))
(assert-equal "(416) 555-1212" ((p'primaryTelcom)'address))
; Part 8 - Remove email
; Result - Primary telcom remains as telephone number
((read-instance training:EmailAddress '() `(= (@ entity) ,p) '())'delete)
(commit)
(reset-context)
(set! p (read-instance training:Person '() '(= lastName "Event") '()))
(assert-equal "(416) 555-1212" ((p'primaryTelcom)'address))
; Part 9 - Remove primary telcom
; Result - Primary telcom set to null
((p'primaryTelcom)'delete)
(commit)
(reset-context)
(set! p (read-instance training:Person '() '(= lastName "Event") '()))
(assert-null ((p'primaryTelcom)'address))
; Part 10 - Delete the person instance
; Result - Person is removed from database.
(p'delete)
(commit)
(assert-null (read-instance training:Person '() '(= lastName "Event") '()))
Javascript
JS
var p = new #"training:Person"({firstName : "Bob", initials : "A", lastName : "Event", primaryLanguage : #"training:LanguageEnum".get.ENGLISH});
var tp = new #"training:TelcomType"({name : "Business"});
; Part 1 - Add email and then add telephone number
; Result - Primary telcom is the email
var email = new #"training:EmailAddress"({entity : p, type : tp, name : tp.name, address : "bsled@appco.com", displayName : "Bob Sled"});
var tel = new #"training:TelephoneNumber"({type : tp, name : tp.name, address : "(416) 555-1212", entity : p, extension : "x123"});
commit();
#"reset-context"();
p = #"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")"), null);
#"assert-equal"("bsled@appco.com", p.primaryTelcom.address);
; Part 2 - Delete the primary telcom
; Result - Primary telcom is reset to phone number
p.primaryTelcom.delete();
commit();
#"reset-context"();
p = #"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")"), null);
#"assert-equal"("(416) 555-1212", p.primaryTelcom.address);
; Part 3 - Delete primary telcom
; Result - Primary Telcom remains null
p.primaryTelcom.delete;
commit();
#"reset-context"();
p = #"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")"), null);
tp = #"read-instance"(#"training:TelcomType", null, scm("'(= name \"Business\")"), null);
#"assert-null"(p.primaryTelcom.address);
; Part 4 - Add telephone number and then add email
; Result - Primary telcom is the telephone number
; test tel added first and removed first
tel = new #"training:TelephoneNumber"({type : tp, name : tp.name, address : "(416) 555-1212", entity : p, extension : "x123"});
email = new #"training:EmailAddress"({entity : p, type : tp, name : tp.name, address : "bsled@appco.com", displayName : "Bob Sled"});
commit();
#"reset-context"();
p = #"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")", null);
#"assert-equal"("(416) 555-1212", p.primaryTelcom.address);
; Part 5 - Delete the primary telcom
; Result - Primary telcom is reset to email
p.primaryTelcom.delete();
commit();
#"reset-context"();
p = #"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")"), null);
#"assert-equal"("bsled@appco.com", p.primaryTelcom.address);
; Part 6 - Delete primary telcom
; Result - Primary Telcom remains null
p.primaryTelcom.delete();
commit();
#"reset-context"();
p = #"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")"), null);
tp = #"read-instance"(#"training:TelcomType", null, scm("'(= name \"Business\")"), null);
#"assert-null"(p.primaryTelcom.address);
; Part 7 - (like part 4) Add telephone number and then add email
; Result - Primary telcom is the telephone number
tel = new #"training:TelephoneNumber"({type : tp, name : tp.name, address : "(416) 555-1212", entity : p, extension : "x123"});
email = new #"training:EmailAddress"({entity : p, type : tp, name : tp.name, address : "bsled@appco.com", displayName : "Bob Sled"});
commit();
#"reset-context"();
p #"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")"), null);
#"assert-equal"("(416) 555-1212", p.primaryTelcom.address);
; Part 8 - Remove email
; Result - Primary telcom remains as telephone number
#"read-instance"(#"training:EmailAddress", null, scm("`(= (@ entity) ,p)"), null).delete);
commit();
#"reset-context"();
p = #"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")", null);
#"assert-equal("(416) 555-1212", p.primaryTelcom.address);
; Part 9 - Remove primary telcom
; Result - Primary telcom set to null
p.primaryTelcom.delete();
commit();
#"reset-context"();
p = #"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")"), null);
#"assert-null"(p.primaryTelcom.address);
; Part 10 - Delete the person instance
; Result - Person is removed from database.
p.delete();
commit();
#"assert-null"(#"read-instance"(#"training:Person", null, scm("'(= lastName \"Event\")"), null);
Review the script.
Save your changes.
Run the unit test. All the test cases execute, including the ones you created in a previous module.
Publish the model
Adding the events does not change the persistence model, so you do not need to update the dump files or database. However, you should increment the model's revision number from 8.1.0.4 to 8.1.0.5 and republish it to ensure that you have a published version that contains all of your changes.
More About Events
In this module, you made use of the create() and delete() events that are inherited from the base class Object.
The full list of events provided by the base class Object is as follows: :class() static - Returns the class object of this instance.
:oid() Returns the oid object of this instance.
commit() Invoked before the instance is committed.
create() Invoked after the instance has been created and initialized.
delete() Invoked to delete the instance.
isNew() Indicates if the instances state is New.
load(attributes) Loads on demand an undefined attribute.
lock() Invoked to lock the instance.
new(values) static - Creates and initializes an instance of this class.
old(attribute) Gets the original value of an attribute as it was read.
openCursor(attributes where orderBy count offset xlock) static - Opens a cursor to read the specified instances of this class.
read(attributes where orderBy count offset xlock) static - Reads the specified instances of this class.
update() Invoked after the instance has been created and initialized.
updated(attribute) Checks whether an attribute value has been updated but not committed.
Info
A static event is not tied to any class instance. It cannot interact with instance variables or call instance events (i.e. non-static events).
JavaScript errors detected
Please note, these errors can depend on your browser setup.
If this problem persists, please contact our support.