Skip to main content
Skip table of contents

Creating events

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:

  1. Open the training:Entity class and create a new attribute called primaryTelcom with the following properties:

    primaryTelcom attribute properties

    AttributePropertyValue
    primaryTelcomTypetraining:Telcom
    Reverseentity
  2. In the Persistence Mapping tab, add an attribute mapping for primaryTelcom.
  3. 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:

  1. Open the training:Telcom class for editing. Go to the Attributes tab and select the isPrimary attribute.
  2. In the Value subtab, remove the Initializer value.
  3. 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.
  4. 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.
  5. In the Common subtab, set the Read-Only attribute to true (selected).
  6. 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:

  1. In the Persistence layer, open the training:DB data source.
  2. Select the Entity table and click the Columns tab.
  3. Add a column with the following properties:

    Column to add to the Entity table

    Column NameTypeAllocationAllow NullsPrecisionCase Insensitive
    primaryTelcombinaryfixedtrue16false
  4. In the Indexes subtab, add an index with the following properties:

    Index to add to the Entity table

    NameTypeUniqueIndex Columns
    TrnEntity.FK_PrimaryTelcomvirtualfalseprimaryTelcom

Modify persistence for the Telcom class

To modify persistence for the training:Telcom class:

  1. Select the training:Telcom table and go to the Columns tab.
  2. 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:

  1. In the Persistence layer on the Data Sources tab right-click the DefaultRelationalDatabase and select Generate Upgrade Steps.
  2. Leave the default values for Upgrade and Current Data Source.
  3. Select Old Model as the Old Data Source. You are going to use a published model as the old model.
  4. 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.
  5. 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.
  6. Enter the following as the Description: Dropped isPrimary column and added primaryTelcom attribute support.
  7. Click Finish to create the upgrade steps.
  8. A warning appears because the upgrade steps include a DropColumn. Click Proceed to continue.
  9. Your upgrade is added to the bottom of the list of upgrades.
  10. 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.
  11. 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.
  12. Click OK. Note that the new version is now reflected in the table in the Models tab.
  13. Click Close.
  14. 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:

  1. In the Business Model layer, open the training:Telcom class for editing.
  2. In the Events tab, click the Override Base Events button
    and add the following events:
    • create()
    • delete ()
  3. Click the Add
    button to add a new event.

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.

  1. Select the create event and click the Actions subtab.
  2. Click the Add button
    and select Before.
    This adds a new action that runs before the main section of the create event.
  3. Set the Name of the action to setPrimary.
  4. 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).

  5. 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.

  6. 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:

  1. In the Events list, select the delete event.
  2. Click the Actions subtab.
  3. Click the Add button
    and select Before. The event runs before the main action of deleting a training:Telcom instance takes place.
  4. Set the Name of the action to clearPrimary.
  5. 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.

  6. 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:

  1. Add a second action for the delete event after clearPrimary in the Actions list.
  2. Set the Type to before.
  3. Set the Name to resetPrimary.
  4. 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.

  5. Enter the following script:

    CODE
    ; reset the entity's primaryTelcom to another telcom if one exists.
    ((@ entity)'primaryTelcom (this'getNextPrimary))

    Javascript

    JS
    this.entity.primaryTelcom = this.getNextPrimary();
  6. Click the Save button

    in the toolbar to save your changes.

Add a main action to the getNextPrimary event

Finally, add a main action to the new getNextPrimary event:

  1. In the Events list, select the getNextPrimary event.
  2. Click the Actions subtab.
  3. Click the Add button
    and select Main.
    This defines the main block of code to run when the event is called.
  4. Leave the Name property as main.
  5. In the Script subtab, enter the following Script:

    CODE
    ; Try phones first, then emails if no phone is available
    (let ((primary (read-instance training:TelephoneNumber '() `(and (not (= (@) ,(@))) (= entity ,(@ entity))) '())))
       (when (null? primary)
          (set! primary (read-instance training:EmailAddress '() `(and (not (= (@) ,(@))) (= entity ,(@ entity))) '()))
       )
       primary ; return
    )

    Javascript

    JS
    var primary = #"read-instance"(#"training:TelephoneNumber, null, scm("`(and (not (= (@) ,(@))) (= entity ,(@ entity)))"), null);
    when (primary == null) {
    	primary = #"read-instance"(#"training:EmailAddress", null, scm("`(and (not (= (@) ,(@))) (= entity ,(@ entity)))"), null);
    }
    return primary;

    Info

    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.

  6. 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:

  1. Open the training:Telcom unit test (Business Model → Unit Tests).
  2. In the Test Cases tab, click the Add button
    to add a new test case.
  3. Name the test case PrimaryDefaulting.
  4. Enter the Description Test primary defaulting.
  5. 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);
  6. Review the script.
  7. Save your changes.
  8. 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.