Skip Headers
Oracle® Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework
11g Release 1 (11.1.1.5.0)

Part Number B31974-10
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

8 Implementing Validation and Business Rules Programmatically

This chapter explains the key entity object events and features for implementing the most common kinds of business rules.

This chapter includes the following sections:

8.1 Introduction to Programmatic Business Rules

Complementing the built-in declarative validation features, entity objects and view objects have method validators and several events you can handle to programmatically implement encapsulated business logic using Java code. These concepts are illustrated in Figure 8-1.

Figure 8-1 Key Entity Objects Features and Events for Programmatic Business Logic

Image shows entity object features

Note:

When coding programmatic business rules, it's important to have a firm grasp of the validation cycle. For more information, see Section 7.2, "Understanding the Validation Cycle."

8.2 Using Method Validators

Method validators are the primary way of supplementing declarative validation rules and Groovy-scripted expressions using your own Java code. Method validators trigger Java code that you write in your own validation methods at the appropriate time during the entity object validation cycle. There are many types of validation you can code with a method validator, either on an attribute or on an entity as a whole.

You can add any number of attribute-level or entity-level method validators, provided they each trigger a distinct method name in your code. All validation method names must begin with the word validate; however, following that rule you are free to name them in any way that most clearly identifies the functionality. For an attribute-level validator, the method must take a single argument of the same type as the entity attribute. For an entity-level validator, the method takes no arguments. The method must also be public, and must return a boolean value. Validation will fail if the method returns false.

Note:

Although it is important to be aware of these rules, when you use JDeveloper to create method validators, JDeveloper creates the correct interface for the class.

At runtime, the Method validator passes an entity attribute to a method implemented in your entity object class.

In Example 8-1, the method accepts strings that start with a capital letter and throws an exception on null values, empty strings, and strings that do not start with a capital letter.

Example 8-1 Method That Validates If the First Letter Is a Capital

public boolean validateIsCapped(String text)
{
  if (text != null && 
      text.length() != 0 && 
      text[0] >= 'A' && 
      text[0] <= 'Z')
  {
    return true;
  }
}

8.2.1 How to Create an Attribute-Level Method Validator

To create an attribute-level Method validator:

  1. In the Application Navigator, double-click the desired entity object.

  2. In the overview editor, click the Java navigation tab.

    The Java page shows the Java generation options that are currently enabled for the entity object. If your entity object does not yet have a custom entity object class, then you must generate one before you can add a Method validator. To generate the custom Java class, click the Edit icon, then select Generate Entity Object Class, and click OK to generate the *.java file.

  3. Click the Business Rules navigation tab, and then expand the Attributes node, and select the attribute that you want to validate.

  4. Click the New icon to add a validation rule.

  5. Select Method from the Rule Type dropdown list.

    The Add Validation Rule dialog displays the expected method signature for an attribute-level validation method. You have two choices:

    • If you already have a method in your entity object's custom Java class of the appropriate signature, it will appear in the list and you can select it after deselecting the Create and Select Method checkbox.

    • If you leave the Create and Select Method checkbox selected (see Figure 8-2), you can enter any method name in the Method Name box that begins with the word validate. When you click OK, JDeveloper adds the method to your entity object's custom Java class with the appropriate signature.

  6. Finally, supply the text of the error message for the default locale that the end user should see if this validation rule fails.

Figure 8-2 Adding an Attribute-Level Method Validator

Image of Add Validation Rule dialog

8.2.2 What Happens When You Create an Attribute-Level Method Validator

When you add a new method validator, JDeveloper updates the XML component definition to reflect the new validation rule. If you asked to have the method created, the method is added to the entity object's custom Java class. Example 8-2 illustrates a simple attribute-level validation rule that ensures that the OrderShippedDate of an order is a date in the current month. Notice that the method accepts an argument of the same type as the corresponding attribute, and that its conditional logic is based on the value of this incoming parameter. When the attribute validator fires, the attribute value has not yet been set to the new value in question, so calling the getOrderShippedDate() method inside the attribute validator for the OrderShippedDate attribute would return the attribute's current value, rather than the candidate value that the client is attempting to set.

Example 8-2 Simple Attribute-Level Method Validator

public boolean validateOrderShippedDate(Date  data) {
  if (data != null && data.compareTo(getFirstDayOfCurrentMonth()) <= 0) {
    return false;
  }
  return true;
}

Note:

The return value of the compareTo() method is zero (0) if the two dates are equal, negative one (-1) if the first date is less than the second, or positive one (1) if the first date is greater than the second.

8.2.3 How to Create an Entity-Level Method Validator

To create an entity-level method validator:

  1. In the Application Navigator, double-click the desired entity object.

  2. In the overview editor, click the Java navigation tab.

    The Java page shows the Java generation options that are currently enabled for the entity object. If your entity object does not yet have a custom entity object class, then you must generate one before you can add a Method validator. To generate the custom Java class, click the Edit icon, then select Generate Entity Object Class, and click OK to generate the *.java file.

  3. Click the Business Rules navigation tab, and then select the Entity node.

  4. Click the New icon to add a validation rule.

  5. Select Method from the Rule Type dropdown list.

    The Add Validation Rule dialog displays the expected method signature for an entity-level validation method. You have two choices:

    • If you already have a method in your entity object's custom Java class of the appropriate signature, it will appear in the list and you can select it after deselecting the Create and Select Method checkbox.

    • If you leave the Create and Select Method checkbox selected (see Figure 8-3), you can enter any method name in the Method Name box that begins with the word validate. When you click OK, JDeveloper adds the method to your entity object's custom Java class with the appropriate signature.

  6. Finally, supply the text of the error message for the default locale that the end user should see if this validation rule fails.

Figure 8-3 Adding an Entity-Level Method Validator

Image of Add Validation rule dialog

8.2.4 What Happens When You Create an Entity-Level Method Validator

When you add a new method validator, JDeveloper updates the XML component definition to reflect the new validation rule. If you asked to have the method created, the method is added to the entity object's custom Java class. Example 8-3 illustrates a simple entity-level validation rule that ensures that the OrderShippedDate of an order comes after the OrderDate.

Example 8-3 Simple Entity-Level Method Validator

public boolean validateOrderShippedDateAfterOrderDate() {
  Date orderShippedDate = getOrderShippedDate();
  Date orderDate  = getOrderDate();
  if (orderShippedDate != null && orderShippedDate.compareTo(orderDate) < 0) {
    return false;
  }
  return true;
}

8.2.5 What You May Need to Know About Translating Validation Rule Error Messages

Like the locale-specific UI control hints for entity object attributes, the validation rule error messages are added to the entity object's component message bundle file. These entries in the message bundle represent the strings for the default locale for your application. To provide translated versions of the validation error messages, follow the same steps as for translating the UI control hints, as described in Section 4.7, "Working with Resource Bundles."

8.3 Assigning Programmatically Derived Attribute Values

When declarative defaulting falls short of your needs, you can perform programmatic defaulting in your entity object:

8.3.1 How to Provide Default Values for New Rows at Create Time

The create() method provides the entity object event you can handle to initialize default values the first time an entity row is created. Example 8-4 shows the overridden create method of the OrderEO entity object in the StoreFront module of the Fusion Order Demo. It calls an attribute setter methods to populate the OrderDate attribute in a new order entity row.

You can also define default values using a Groovy expression. For more information, see Section 4.10.6, "How to Define a Static Default Value."

Example 8-4 Programmatically Defaulting Attribute Values for New Rows

// In OrderEOImpl.java in Fusion Order Demo 
protected void create(AttributeList nameValuePair) {
   super.create(nameValuePair);
   this.setOrderDate(new Date());
}

Note:

Calling the setAttribute() method inside the overridden create() method does not mark the new row as being changed by the user. These programmatically assigned defaults behave like declaratively assigned defaults.

8.3.1.1 Choosing Between create() and initDefaultExpressionAttributes() Methods

You should override the initDefaultExpressionAttributes() method for programmatic defaulting logic that you want to fire both when the row is first created, and when it might be refreshed back to initialized status.

If an entity row has New status and you call the refresh() method on it, then the entity row is returned to an Initialized status if you do not supply either the REFRESH_REMOVE_NEW_ROWS or REFRESH_FORGET_NEW_ROWS flag. As part of this process, the entity object's initDefaultExpressionAttributes() method is invoked, but not its create() method again.

8.3.1.2 Eagerly Defaulting an Attribute Value from a Database Sequence

Section 4.10.9, "How to Synchronize with Trigger-Assigned Values," explains how to use the DBSequence type for primary key attributes whose values need to be populated by a database sequence at commit time. Sometimes you may want to eagerly allocate a sequence number at entity row creation time so that the user can see its value and so that this value does not change when the data is saved. To accomplish this, use the SequenceImpl helper class in the oracle.jbo.server package in an overridden create() method as shown in Example 8-5. It shows code from the custom Java class of the WarehouseEO entity object in the StoreFront module of the Fusion Order Demo. After calling super.create(), it creates a new instance of the SequenceImpl object, passing the sequence name and the current transaction object. Then it calls the setWarehouseId() attribute setter method with the return value from SequenceImpl's getSequenceNumber() method.

Note:

For a metadata-driven alternative to this approach, see Section 4.12.5, "Assigning the Primary Key Value Using an Oracle Sequence."

Example 8-5 Eagerly Defaulting an Attribute's Value from a Sequence at Create Time

// In WarehouseEOImpl.java
import oracle.jbo.server.SequenceImpl;
// Default WarehouseId value from WAREHOUSE_SEQ sequence at entity row create time
protected void create(AttributeList attributeList) {
    super.create(attributeList);
    SequenceImpl sequence = new SequenceImpl("WAREHOUSE_SEQ",getDBTransaction());
    setWarehouseId(sequence.getSequenceNumber());
}

8.3.2 How to Assign Derived Values Before Saving

If you want to assign programmatic defaults for entity object attribute values before a row is saved, override the prepareForDML() method and call the appropriate attribute setter methods to populate the derived attribute values. To perform the assignment only during INSERT, UPDATE, or DELETE, you can compare the value of the operation parameter passed to this method against the integer constants DML_INSERT, DML_UPDATE, DML_DELETE respectively.

Example 8-6 shows an overridden prepareForDML() method that assigns derived values.

Example 8-6 Assigning Derived Values Before Saving Using PrepareForDML

protected void prepareForDML(int operation, TransactionEvent e) {
  super.prepareForDML(operation, e);
  //Populate GL Date
  if (operation == DML_INSERT) {
    if (this.getGlDate() == null) {
      String glDateDefaultOption = 
        (String)this.getInvoiceOption().getAttribute("DefaultGlDateBasis");
      if ("I".equals(glDateDefaultOption)) {
        setAttribute(GLDATE, this.getInvoiceDate());
      } else {
        setAttribute(GLDATE, this.getCurrentDBDate());
      }
    }
  }
 
  //Populate Exchange Rate and Base Amount if null
  if ((operation == DML_INSERT) || (operation == DML_UPDATE)) {
    BigDecimal defaultExchangeRate = new BigDecimal(1.5);
    if ("Y".equals(this.getInvoiceOption().getAttribute("UseForeignCurTrx"))) {
      if (!(this.getInvoiceCurrencyCode().equals(
                            this.getLedger().getAttribute("CurrencyCode")))) {
        if (this.getExchangeDate() == null) {
          setAttribute(EXCHANGEDATE, this.getInvoiceDate());
        }
        if (this.getExchangeRateType() == null) {
          String defaultConvRateType = 
            (String)this.getInvoiceOption().getAttribute("DefaultConvRateType");
          if (defaultConvRateType != null) {
            setAttribute(EXCHANGERATETYPE, defaultConvRateType);
          } else {
            setAttribute(EXCHANGERATETYPE, "User");
          }
        }
        if (this.getExchangeRate() == null) {
          setAttribute(EXCHANGERATE, defaultExchangeRate);
        }
        if ((this.getExchangeRate() != null) && 
            (this.getInvoiceAmount() != null)) {
          setAttribute(INVAMOUNTFUNCCURR, 
                     (this.getExchangeRate().multiply(this.getInvoiceAmount())));
        }
      } else {
        setAttribute(EXCHANGEDATE, null);
        setAttribute(EXCHANGERATETYPE, null);
        setAttribute(EXCHANGERATE, null);
        setAttribute(INVAMOUNTFUNCCURR, null);
      }
    }
  }
}

8.3.3 How to Assign Derived Values When an Attribute Value Is Set

To assign derived attribute values whenever another attribute's value is set, add code to the latter attribute's setter method. Example 8-7 shows the setter method for an AssignedTo attribute in an entity object.

Example 8-7 Setting the Assigned Date Whenever the AssignedTo Attribute Changes

public void setAssignedTo(Number value) {
  setAttributeInternal(ASSIGNEDTO, value);
  setAssignedDate(getCurrentDateWithTime());
}

After the call to setAttributeInternal() to set the value of the AssignedTo attribute, it uses the setter method for the AssignedDate attribute to set its value to the current date and time.

Note:

It is safe to add custom code to the generated attribute getter and setter methods as shown here. When JDeveloper modifies code in your class, it intelligently leaves your custom code in place.

8.4 Undoing Pending Changes to an Entity Using the Refresh Method

You can use the refresh(int flag) method on a row to refresh any pending changes it might have. The behavior of the refresh() method depends on the flag that you pass as a parameter. The three key flag values that control its behavior are the following constants in the Row interface:

8.4.1 How to Control What Happens to New Rows During a Refresh

By default, any entity rows with New status that you refresh() are reverted back to blank rows in the Initialized state. Declarative defaults are reset, as well as programmatic defaults coded in the initDefaultExpressionAttributes() method, but the entity object's create() method is not invoked during this blanking-out process.

You can change this default behavior by combining one of the flags in Section 8.4 with one of the following two flags (using the bitwise-OR operator):

  • REFRESH_REMOVE_NEW_ROWS, new rows are removed during refresh.

  • REFRESH_FORGET_NEW_ROWS, new rows are marked Dead.

8.4.2 How to Cascade Refresh to Composed Children Entity Rows

You can cause a refresh() operation to cascade to composed child entity rows by combining the REFRESH_CONTAINEES flag (using the bitwise-OR operator) with any of the valid flag combinations described in Section 8.4 and Section 8.4.1. This causes the entity to invoke refresh() using the same mode on any composed child entities it contains.

8.5 Using View Objects for Validation

When your business logic requires performing SQL queries, the natural choice is to use a view object to perform that task. Keep in mind that the SQL statements you execute for validation will "see" pending changes in the entity cache only if they are entity-based view objects. Read-only view objects will only retrieve data that has been posted to the database.

8.5.1 How to Use View Accessors for Validation Against View Objects

Since entity objects are designed to be reused in any application scenario, they should not depend directly on a view object instance in any specific application module's data model. Doing so would prevent them from being reused in other application modules, which is highly undesirable.

Instead, you should use a view accessor to validate against a view object. For more information, see Section 10.4.1, "How to Create a View Accessor for an Entity Object or View Object."

Using a view accessor, your validation code can access the view object and set bind variables, as shown in Example 8-8.

Example 8-8 Using a Validation View Object in a Method Validator

// Sample entity-level validation method
public boolean validateSomethingUsingViewAccessor() { 
  RowSet rs = getMyValidationVO();
  rs.setNamedBindParameter("Name1", value1); 
  rs.setNamedBindParameter("Name2", value2); 
  rs.executeQuery(); 
  if ( /* some condition */) { 
  /* 
   * code here returns true if the validation succeeds 
   */ 
  } 
  return false; 
} 

Best Practice:

Any time you access a row set programmatically, you should consider creating a secondary iterator for the row set. This ensures that you will not disturb the current row set of the default row set iterator that may be utilized when your expose your view objects as data controls to the user interface project. You can call createRowSetIterator() on the row set you are working with to create a secondary named row set iterator. When you are through with programmatic iteration, your code should call closeRowSetIterator() on the row set to remove the secondary iterator from memory.

As the sample code suggests, view objects used for validation typically have one or more named bind variables in them. In this example, the bind variables are set using the setNamedBindParameter() method. However, you can also set these variables declaratively in JDeveloper using Groovy expressions in the view accessor definition page.

Depending on the kind of data your view object retrieves, the "/* some condition */" expression in the example will look different. For example, if your view object's SQL query is selecting a COUNT() or some other aggregate, the condition will typically use the rs.first() method to access the first row, then use the getAttribute() method to access the attribute value to see what the database returned for the count.

If the validation succeeds or fails based on whether the query has returned zero or one row, the condition might simply test whether rs.first() returns null or not. If rs.first() returns null, there is no "first" row. In other words, the query retrieved no rows. In other cases, you may be iterating over one or more query results retrieved by the view object to determine whether the validation succeeds or fails.

8.5.2 How to Validate Conditions Related to All Entities of a Given Type

The beforeCommit() method is invoked on each entity row in the pending changes list after the changes have been posted to the database, but before they are committed. This can be a useful method in which to execute view object based validations that must assert some rule over all entity rows of a given type.

Note:

You can also do this declaratively using a transaction-level validator (see Section 7.6.4, "How to Set Transaction-Level Validation").

If your beforeCommit() logic can throw a ValidationException, you must set the jbo.txn.handleafterpostexc property to true in your configuration to have the framework automatically handle rolling back the in-memory state of the other entity objects that may have already successfully posted to the database (but not yet been committed) during the current commit cycle.

8.5.3 What You May Need to Know About Row Set Access with View Accessors

If your entity object or view object business logic iterates over its own view accessor row set, and that view accessor is not also used by a model-defined List of Values, then there is no need to use a secondary row set iterator. For example, if an entity object has a view accessor named AirportValidationVA for a view object that takes one named bind parameter, it can iterate its own view accessor row set using either Groovy script or Java. Example 8-9 show a Groovy script that iterates over a view accessor row set.

Example 8-9 Using a View Accessor in Groovy Script

AirportValidationVA.setNamedWhereClauseParam("VarTla",newValue)
AirportValidationVA.executeQuery();
return AirportValidationVA.first() != null;

Example 8-10 shows a Java method validator that iterates over a view accessor row set.

Example 8-10 Using a View Accessor in a Method Validator

public boolean validateJob(String job) {
   getAirportValidationVA().setNamedWhereClauseParam("VarTla",job);
   getAirportValidationVA().executeQuery();
   return getAirportValidationVA().first() != null;
}

8.6 Accessing Related Entity Rows Using Association Accessors

To access information from related entity objects, you use an association accessor method in your entity object's custom Java class. By calling the accessor method, you can easily access any related entity row — or set of entity rows — depending on the cardinality of the association.

8.6.1 How to Access Related Entity Rows

You can use an association accessor to access related entity rows. Example 8-11 shows code from the ControllingPostingOrder project in the AdvancedEntityExamples module of the Fusion Order Demo that shows the overridden postChanges() method in the ProductsBase entity object's custom Java class. It uses the getSupplier() association accessor to retrieve the related supplier for the product.

Example 8-11 Accessing a Parent Entity Row In a Create Method

// In ProducstBaseImpl.java in the ControllingPostingOrder project 
// of the Fusion Order Demo Advanced Entity Examples
@Override
public void postChanges(TransactionEvent transactionEvent) {
  /* If current entity is new or modified */
  if (getPostState() == STATUS_NEW || getPostState() == STATUS_MODIFIED) {
    /* Get the associated supplier for the product */
    SuppliersImpl supplier = getSupplier();
    /* If there is an associated product */
    if (supplier != null) {
      /* And if it's post-status is NEW */
      if (supplier.getPostState() == STATUS_NEW) {
        /* Post the supplier first, before posting this entity */
        supplier.postChanges(transactionEvent);
      }
    }
  }
  super.postChanges(transactionEvent);
}

8.6.2 How to Access Related Entity Row Sets

If the cardinality of the association is such that multiple rows are returned, you can use the association accessor to return sets of entity rows.

Example 8-12 illustrates the code for the overridden postChanges() method in the Suppliers entity object's custom Java class. It shows the use of the getProductsBase() association accessor to retrieve the RowSet object of ProductsBase rows in order to update the SupplierId attribute in each row using the setSupplierId() association accessor.

Example 8-12 Accessing a Related Entity Row Set Using an Association Accessor

// In SuppliersImpl.java in the ControllingPostingOrder project 
// of the Fusion Order Demo Advanced Entity Examples
RowSet newProductsBeforePost = null;
@Override
public void postChanges(TransactionEvent transactionEvent) {
  /* Only update references if Supplier is new */
  if (getPostState() == STATUS_NEW) {
   /*
    * Get a rowset of products related to this new supplier before calling super
    */
    newProductsBeforePost = (RowSet)getProductsBase();
  }
  super.postChanges(transactionEvent);
}

...

protected void refreshFKInNewContainees() {
  if (newProductsBeforePost != null) {
    Number newSupplierId = getSupplierId().getSequenceNumber();
    /* 
     * Process the rowset of suppliers that referenced the new product prior 
     * to posting, and update their ProdId attribute to reflect the refreshed
     * ProdId value that was assigned by a database sequence during posting.
     */
    while (newProductsBeforePost.hasNext()){
      ProductsBaseImpl product = (ProductsBaseImpl)newProductsBeforePost.next();
      product.setSupplierId(newSupplierId);
    }
    closeNewProductRowSet();
  }  
}

8.7 Referencing Information About the Authenticated User

If you have run the Configure ADF Security wizard on your application to enable the ADF authentication servlet to support user login and logout, the oracle.jbo.server.SessionImpl object provides methods you can use to get information about the name of the authenticated user and about the roles of which they are a member. This is the implementation class for the oracle.jbo.Session interface that clients can access.

For information about how to access information about the authenticated user, see Section 30.11.3.3, "How to Determine the Current User Name, Enterprise Name, or Enterprise ID" and Section 30.11.3.4, "How to Determine Membership of a Java EE Security Role".

For more information about security features in Oracle Fusion Web Applications, read Chapter 30, "Enabling ADF Security in a Fusion Web Application."

8.8 Accessing Original Attribute Values

If an entity attribute's value has been changed in the current transaction, when you call the attribute getter method for it you will get the pending changed value. Sometimes you want to get the original value before it was changed. Using the getPostedAttribute() method, your entity object business logic can consult the original value for any attribute as it was read from the database before the entity row was modified. This method takes the attribute index as an argument, so pass the appropriate generated attribute index enums that JDeveloper maintains for you.

8.9 Storing Information About the Current User Session

If you need to store information related to the current user session in a way that entity object business logic can reference, you can use the user data hash table provided by the Session object.

8.9.1 How to Store Information About the Current User Session

When a new user accesses an application module for the first time, the prepareSession() method is called. As shown in Example 8-13, the application module overrides prepareSession() to retrieve information about the authenticated user by calling a retrieveUserInfoForAuthenticatedUser() method on the view object instance. Then, it calls the setUserIdIntoUserDataHashtable() helper method to save the user's numerical ID into the user data hash table.

Example 8-13 Overriding prepareSession() to Query User Information

// In the application module
protected void prepareSession(Session session) {
  super.prepareSession(session);
  /*
   * Query the correct row in the VO based on the currently logged-in
   * user, using a custom method on the view object component
   */
  getLoggedInUser().retrieveUserInfoForAuthenticatedUser();     
  setUserIdIntoUserDataHashtable();
}

Example 8-14 shows the code for the view object's retrieveUserInfoForAuthenticatedUser() method. It sets its own EmailAddress bind variable to the name of the authenticated user from the session and then calls executeQuery() to retrieve the additional user information from the USERS table.

Example 8-14 Accessing Authenticated User Name to Retrieve Additional User Details

// In the view object's custom Java class
public void retrieveUserInfoForAuthenticatedUser() {
  SessionImpl session = (SessionImpl)getDBTransaction().getSession();
  setEmailAddress(session.getUserPrincipalName());
  executeQuery();
  first();
}

One of the pieces of information about the authenticated user that the view object retrieves is the user's numerical ID number, which that method returns as its result. For example, the user sking has the numeric UserId of 300.

Example 8-15 shows the setUserIdIntoUserDataHashtable() helper method — used by the prepareSession() code in Example 8-13 — that stores this numerical user ID in the user data hash table, using the key provided by the string constant CURRENT_USER_ID.

Example 8-15 Setting Information into the UserData Hashtable for Access By Entity Objects

// In the application module
private void setUserIdIntoUserDataHashtable() {
  Integer userid = getUserIdForLoggedInUser();
  Hashtable userdata = getDBTransaction().getSession().getUserData();
  userdata.put(CURRENT_USER_ID, userid);
}  

The corresponding entity objects in this example can have an overridden create() method that references this numerical user ID using a helper method like the one in Example 8-16 to set the CreatedBy attribute programmatically to the value of the currently authenticated user's numerical user ID.

Example 8-16 Referencing the Current User ID in a Helper Method

protected Number getCurrentUserId() {
  Hashtable userdata = getDBTransaction().getSession().getUserData();
  Integer userId = (Integer)userdata.get(CURRENT_USER_ID);
  return userdata != null ? Utils.intToNumber(userId):null;
}

8.9.2 How to Use Groovy to Access Information About the Current User Session

The top-level adf object allows you access to objects that the framework makes available to Groovy script. The adf.userSession object returns a reference to the ADF Business Components user session, which you can use to reference values in the userData hash map that is part of the session.

Example 8-17 shows the Groovy script you would use to reference a userData hash map key named MyKey.

Example 8-17 Accessing the Current User Session Using Groovy Script

adf.userSession.userData.MyKey

8.10 Accessing the Current Date and Time

You might find it useful to reference the current date and time in your entity object business logic. You can reference the current date or current date and time using the following Groovy script expressions:

For more information about using Groovy script in your entity object business logic, see Section 3.6, "Overview of Groovy Support."

8.11 Sending Notifications Upon a Successful Commit

The afterCommit() method is invoked on each entity row that was in the pending changes list and got successfully saved to the database. You can use this method to send a notification on a commit.

A better way to send notifications upon a successful commit is by declaring a business event. For more information on how to create a business event, see Section 4.11, "Creating Business Events."

8.12 Conditionally Preventing an Entity Row from Being Removed

Before an entity row is removed, the remove() method is invoked on an entity row. You can throw a JboException in the remove() method to prevent a row from being removed if the appropriate conditions are not met.

For example, you can add a test in the remove() method that determines the state of the entity object and allows the removal only if it is a new record. Example 8-18 demonstrates this technique.

Note:

This example is in the AddressesImpl.java file in the ConditionalDelete project of the DevGuideExamples workspace in the StandaloneExamples module of the Fusion Order Demo application.

Example 8-18 Overriding the remove() Method to Verify Entity Status Before Removal

// In the Addresses entity object custom Java class
private boolean isDeleteAllowed() {
    byte s = this.getEntityState();
  return s==STATUS_NEW;
}
 
/**
 * Add entity remove logic in this method.
 */
public void remove() {
  if (isDeleteAllowed())
    super.remove();
  else
      throw new JboException("Delete not allowed in this view");
}

Note:

The entity object offers declarative prevention of deleting a master entity row that has existing, composed children rows. You configure this option on the Relationship page of the overview editor for the association.

8.13 Determining Conditional Updatability for Attributes

You can override the isAttributeUpdateable() method in your entity object class to programmatically determine whether a given attribute is updatable or not at runtime based on appropriate conditions.

Example 8-19 shows how an entity object can override the isAttributeUpdateable() method to enforce that its PersonTypeCode attribute is updatable only if the current authenticated user is a staff member. Notice that when the entity object fires this method, it passes in the integer attribute index whose updatability is being considered.

You can implement conditional updatability logic for a particular attribute inside an if or switch statement based on the attribute index. Here PERSONTYPECODE is referencing the integer attribute index enums that JDeveloper maintains in your entity object custom Java class.

Example 8-19 Conditionally Determining an Attribute's Updatability at Runtime

// In the entity object custom Java class
public boolean isAttributeUpdateable(int index) {
  if (index == PERSONTYPECODE) {
    if (!currentUserIsStaffMember()) {
      return super.isAttributeUpdateable(index);
    }
    return CUSTOMER_TYPE.equals(getPersonTypeCode()) ? false : true;
  }
  return super.isAttributeUpdateable(index);
}

Note:

Entity-based view objects inherit this conditional updatability as they do everything else encapsulated in your entity objects. Should you need to implement this type of conditional updatability logic in a way that is specific to a transient view object attribute, or to enforce some condition that involves data from multiple entity objects participating in the view object, you can override this same method in a view object's view row class to achieve the desired result.