Wednesday 10 February 2010

ADF BC Groovy – showing old values along with new part II – via ViewRowImpl

A while back I posted about using Groovy within our ADF BC EOs to expose the new and old values of a particular attribute. This method described in that post works fine if you've access to the EOs in the same project. However in our current project EOs are segmented in another application workspace project for reuse and ADF Library JARed into our existing "master" ADF BC workspace project. The external EOs are intended to be shared by multiple other sub-systems, and it really didn't seem ideal to include additional transient attributes on all the attributes in the shared EOs just in case some other application needs them, mostly they wont want this specific requirement.

This lead to the requirement to include the same functionality, namely transient attributes based on a Groovy expression to return the old value of the attributes, from our project's VOs instead. In other words, if a VO needs them, it includes 'em.

We immediately hit a problem on implementing this requirement. The EntityImpl.getPostedAttribute(index) method is a protected method, and there is no equivalent ViewRowImpl method. Arguably it should be provided at the ViewRowImpl level, and a search of the JDev OTN forums shows it's an old issue and I guess not likely to change anytime soon.

Without an immediate change to the ADF BC framework, we needed to find another solution. What we came up with:

(As per the original post we'll assume we're looking at the Employees EO and Position attribute)

1) By default extend the ADF Business Components Framework as per section 37.1 Globally Extending ADF Business Components Functionality of the Fusion Guide.

2) In the extended EntityImpl class, say common.model.CommonEntityImpl, create a public exposed version of the getPostedAttribute method
public Object getPostedAttribute(int index) {
return super.getPostedAttribute(index);
}

3) For the specific VO interested in showing the old values of an attribute, create the associated ViewRowImpl (say EmployeesViewRowImpl). Ensure in the generated class an accessor method to access the underlying EntityImpl has been created, ie:
public EntityImpl getEmployees() {
return (EntityImpl)getEntity(0);
}

4) In the local project's VO transient attributes to show the old values base it on the following Groovy expression:

adf.object.getEmployees().getPostedAttribute(model.entities.EmployeesImpl.POSITION)

What it does:

a) adf.object.getEmployees() calls the EmployeesViewRowImpl.getEmployees() method we checked existed in step 3, to retrieve the associated EntityImpl for the current ViewRowImpl

Note if you haven't undertaken step 3 to generate the associated ViewRowImpl, alternatively you could just call getEntity(0) instead in your Groovy expression. However I'd be cautious on doing this as you're relying on future upgrades of the framework not changing what's at position zero.

b) .getPostedAttribute() calls the now publically exposed getPostedAttribute method we created in the common.model.CommonEntityImpl that gives us the ability to get at old attribute values in the EO

c) The parameter model.entities.EmployeesImpl.POSITION grabs the index position of the Surname field in the EO. Note we need to use the EntityImpl field position rather than the ViewRowImpl field positions, as the order of the fields can be different.


Of interest to this post, an old discussion on the ADF Enterprise Methodology Group, was do most projects extend the ADF framework as per section 37.1 Globally Extending ADF Business Components Functionality? As can be seen from this specific blog post this shows another reason to include the extended classes from day 1 of any ADF development.


Final caveat: Please note this post is mostly for self documentation purposes, posted here so others can make use of it, but is not guaranteed to work; honestly I haven't tested this thoroughly nor has it been placed in a production environment yet. As such be careful to check it works for you, your mileage may vary. If you find any problems it would be appreciated if you could post your findings here to assist other readers.

5 comments:

John Stegeman said...

Chris,

cough, cough *hack* *hack*

For some reason, I get a *hacking* cough when reading this.

No reason that this won't work, but one of the reasons of protected methods is to hide things from callers that you shouldn't rely on, so you're breaking that principle.

Another thought... as you've written this, it won't work directly in the general case, say for example, if you have a VO based upon more than one EO, as you explicitly call getEntity(0).

This may work for your specific case; I just wanted to point out those issues.

Best,

John

Chris Muir said...

Hi John

That's what I thought I pointed out, it's my specific use case, that's why I spelt it out in the opening paragraph.

Regards your point about the protected method, in this case I don't see any issue in doing this. The method being protected, as you know, would therefore be available to the EntityImpl to use. As such if the method's functionality changed, it would break the valid EO case in the original part I post, as much as it would break the VO usage. As such we're as much (in the limited assurance we have Oracle wont change how methods work) protected by this protected method's functionality changing if used by either - it's the same risk.

However I'll agree that pointing this out to beginners isn't necessarily a good idea, but as I said at the end of the post - your mileage may vary.

Finally I'm not sure I get what you're saying about the getEntity(0) method? In step 3 I explicitly tell the reader to check that the framework has created a named accessor (ie. getEmployees - that example was created by the framework) and use that instead, rather than calling getEntity(0) directly in step 4. As I said "I'd be cautious on doing this [using getEntity(0) directly] as you're relying on future upgrades of the framework not changing what's at position zero." Obviously if the framework generates an accessor method for us to use then we should use that instead, which will protect us against the internal mechanics changing, based on the assumption if Oracle does change the functionality, in the upgrade utility, they will change the accessor methods appropriately too.

Cheers,

CM.

John Stegeman said...

Hi Chris,

getEntity(0) -> what if the VO is based upon multiple entities, and the attribute you're interested in comes from one other than the first...

Chris Muir said...

That'd be the exact example where you should use the supplied accessor? Still not sure what you're trying to emphasize when I did it in the original post. I've told the reader to use the accessor, the one generated by JDeveloper, to avoid issues where getEntity(0) doesn't perform as expected. Obviously if the accessor then calls getEntity(0) and it doesn't return the primary EO for the VO, then it would be a bug in ADF.

John, I think our crossroads here is you'r etrying to emphasize protecting programmers from themselves, while in this post I'm more interested in the technical technique.

CM.

Don Kleppinger said...

You can create a transient attribute in the Entity that displays the posted value using a Groovy expression

Put this in the expression

adf.object.getAttribute(1,(Byte)1)

First attribute is attribute index, 2nd attribute 1 means ORIGINAL_VERSION 0 means CURRENT_VERSION