Tuesday 8 July 2008

ADF BC: EO/VO initial state post create() explained

Thanks to Steve Muench, Olivier and Rob on the JDev OTN Forums, I learnt some valuable lessons about the state of ADF Business Component (ADF BC) Entity Objects (EOs) after the create() method is called. I thought I'd share this new knowledge for others to benefit.

In JDeveloper's ADF BC Entity Objects (EOs) and View Objects (VOs), on instantiating an EO/VO, it's possible to set the default value for the EO/VO attributes declaratively or programmatically through code. The declarative fashion is done by setting the Default property to simple literals for the individual EO/VO attributes in the associated EO/VO editor. Within JDev 11g this has been expanded to support Groovy expressions.

Alternatively both the EntityImpl and ViewRowImpl that represent the EO/VOs in code, the programmer can override the create() method to programmatically set the EO/VO attributes. For example you may wish to set an attribute to the next sequence number from a sequence in the Oracle database, and some other attribute to a static value:

public MyEntityImpl extends EntityImpl {

  public void create(AttributeList attributeList) {
    super.create(attributeList);
    SequenceImpl seq = new SequenceImpl("my_db_seq", getDBTransaction());
    setSomeId(seq.getSequenceNumber());
    setSomeField("some value");
  }
  .. and so on ..
}


Thus in the Business Components Browser if you open the associated VO, and create a record, you'll see the defaulted attributes set to the values above.

As per section "9.2.5 Understanding Entity Object Row States" in the 10g JDev guide for Forms/4GL Programmers (this also exists in the 11g guide, but at the time of writing this article the 11g guide is still in draft), on creating a new record the EO's state should be set to STATUS_NEW. STATUS_NEW says that at least 1 of the attributes of the EO has been updated, and thus the record is a candidate to be inserted to the database.

There are other alternative statuses for an EO, including STATUS_INITIALIZED. This status indicates a new record that is not yet a candidate to insert into the database as no changes have yet occurred to its attributes, it's basically just empty. Again to be clear, ADF BC will only mark the record to be a candidate to insert into the database (STATUS_NEW) when either the user sets a value, or programmatically overrides the status of the new row. (The user in this case means the consumer, or the program that is interfacing to the ADF BC project; this won't be a real human user directly, at the very least an UI will sit in front and provide access to the user)

So from our example above, given that we've programmatically updated attributes, is it reasonable to assume the EO's status will be STATUS_NEW once the user has created the record and we've programmatically defaulted the values?

If we consider the Business Components Browser (the Tester), and we consider JDev 10.1.3 the answer is Yes. However if we consider JDev 11g the answer is No. (Groan I hear you say ;)

Why? The key is the statement above:

"ADF BC will only mark the record to be a candidate to insert into the database (STATUS_NEW) when either the user sets a value manually, or programmatically overrides the status of the new row. (The user in this case means the consumer, or the program that is interfacing to the ADF BC project; this won't be a real human user directly, at the very least an UI will sit in front and provide access to the user)"

Note my emphasis, that the calling program can override the EO state if desired.

In JDev 10.1.3 the Business Components Browser (known as the Tester), as confirmed by Steve Muench is a "cheater" and doesn't actually implement a full blown ADFm binding layer, acting like a true ViewController to the ADF BC Model project. Instead it directly invoked the ADF BC project with its own functionality.

In JDev 11g the Business Components Browser sits on a real ADFm binding layer and as such acts like any ADF Faces/RC page.

The ADFm binding layer being the calling program to the ADF BC layer in this case, actually has code in it to override the status of an EO record if it can't detect any attribute changes by the real "human" user, regardless of the programmatic defaultedv values in the code above. And it sets the EO status back to STATUS_INITIALIZED, marking the record as a non-candidate for database inserts.

Conversely the 10.1.3 Business Components Browser because of its own custom functionality didn't.

This means in 10.1.3 when you commit the new-programmatically-defaulted but not user changed EO record, it will be inserted into the database. Conversely in 11g even though you commit the new record, unless you as the user make a change to one or more of the attributes, the 11g ADFm binding layer will override the status of the EO and it won't be written to the database.

Obviously this functionality isn't always desired. To override this default behaviour such that the record is inserted into the database regardless, you can override the ViewRowImpl's setNewRowState() method as follows:

@Override
public void setNewRowState(byte b) {
  if (b != Row.STATUS_INITIALIZED || getNewRowState() != Row.STATUS_NEW) {
  super.setNewRowState(b);
  }
}


It's this method that's called by the ADFm binding layer, so effectively you're intercepting the status set.

Ideally I'd like to see this as a property on the EO/VO editor, but at the very least ADF BC gives us the ability to override the default functionality by code.

Please note for the simplification of the discussion above, when I say that the ADFm binding layer sets the EO status, it can't actually do this directly. It needs to interface with the parent Application Module, then the View Object that represents the EO. Thus why we override the setNewRowState() method in the VO rather than the EO.

Thanks to Steve, Olivier and Rob for their assistance with this post.

This post was written against JDev 10.1.3 and JDev 11gTP4. As usual check everything under your specific version as your mileage may vary.

3 comments:

Chris Muir said...

Subsequent note: I forgot to describe what the override to the setNewRowState does. Effectively if the status being set is STATUS_INITIALIZED but the current row's status is STATUS_NEW, it simply doesn't allow the change. All other status changes are however allowed.

CM.

Hasim said...

Hey CM,
We had this magic code in 11...2 and it was allowing us to create multiple child record without saving the changes. Now we migrated to 11....4 and it is is throwing lot of exceptions like item is required.

You can try to run same app in 11...4.
Thanks,
Hasim

Chris Muir said...

Hi Hasim

Apologies but I don't have an immediate answer to your problem. I suggest you post a question to the OTN forums.

Regards,

CM.