The Power of Properties

Most ADF BC users are traditional Oracle developers–Forms or PL/SQL developers–who are relatively new to Java and Java EE (some, at this point, have lots of Java expertise, but even those usually have still more experience with an older Oracle technology). Because of this, when they miss out on one of the greatest powers of Java, reusability, they often don’t realize, or at least appreciate, what they’re missing out on. So the fact that ADF BC doesn’t openly encourage designing custom code for reusability gets missed.

But this is emphatically not to say that ADF BC doesn’t enable reusability, or even that it’s not documented. It’s just not called out in ways that would encourage a novice, or even intermediate,¬†ADF BC developer to take advantage of it.

Now, before I go on, I should probably point something out. ADF (including ADF BC) is a framework. Every time you create an entity object, for example, you are, in fact, reusing a whole giant wodge of code–the code in oracle.jbo.server.EntityImpl, for one thing, plus the zillions of ADF BC framework classes that support it. Nathalie Rothman of iAdvise pointed this out to me, and although at first I thought it was a trivial point, on second thought, it really isn’t. You should be aware that simply by using ADF, by either extending its base classes or just declaratively customizing particular definitions, you are taking advantage of reusability in a serious way.

But that’s not what I’m talking about here–I’m talking about reusing your own code. And I want to highlight a phenomenal tool for refactoring seemingly component-specific behavior up the hierarchy so it can be used again and again: ADF BC custom properties.

You can add properties, specifying a name and value, to application module definitions, view object definitions, entity object definitions, and view and entity attributes. Why would you want to do this? Well, as the ADF Developer’s Guide explains, you can access these custom properties in generic framework classes to dynamically discover metadata.

How powerful is that? Pretty powerful. It lets you generify a lot of functionality that otherwise you’d have to repeat in each of a number of particular business components custom classes. I’ll give an example–a much more serious example (IMO) than the little bitty toy case Oracle provides.

The Problem

Entity objects write data to the database using standard DML operations–INSERT, UPDATE, and DELETE. That’s fine for most uses, but sometimes you want to perform database changes via a PL/SQL package API. ADF does not provide any 100% declarative way of doing that, despite the fact that that’s something a lot of people want to do; in fact, some organizations want all access to database data to be through package functions and procedures.

Not to worry, though, the ADF Developer’s Guide contains a way to create a common, base entity object class to use for entity object definitions that use a package API instead of DML. But that common class doesn’t actually call the stored procedures; it just calls methods–callInsertProcedure(), callDeleteProcedure(), and callUpdateProcedure(), at appropriate times. To actually use stored procedures for a particular entity object definition, you have to override some or all of these methods to create, set parameters for, and execute particular JDBC statements. After all, the base framework class doesn’t actually know which JDBC statements to execute, which attributes to plug in, or the order to plug them in for any particular entity object definition.

That’s fine if you have, say, five entity object definitions that use stored procedures. But what do you do if, across your enterprise, you have hundreds of entity object definitions that use stored procedures? Do you really have to write this Java code for every single one of them?

Properties to the Rescue

In fact, you don’t. You can create a pair of base classes–one entity object class, and one entity definition class, that makes setup of individual entity object definitions that use PL/SQL procedures 100% declarative. I’m not going to go through the entire procedure for doing that here [edit 7/30/09: You can see it here, though]. What I am going to do is show you the entity definition class, which should at least give you an idea of how it works.

First, I’m going to give you just a skeleton of the entity definition class, so that you can see the fields. We’ll flesh out the rest later.

public class PlSqlEntityDefImpl extends EntityDefImpl {
    private String insertProc;
    private String updateProc;
    private String deleteProc;
    private List<AttributeDef> insertParams;
    private List<AttributeDef> updateParams;
    private List<AttributeDef> deleteParams;
}

The class should contain getters for all of these fields, too; they’ll be used by the custom entity object class.

What are these fields for? For each particular entity object definition, the three String fields will store the names of the stored procedures (if any) used instead of insert, update, and delete. The three List fields will store the entity object attributes, in order, that should be passed to the procedures. If one of the String fields is null, the entity object can just fall back on the framework default DML behavior for that operation.

The finishedLoading() Method

EntityDefImpl calls its finishedLoading() method when it has completed loading of the XML metadata. That’s when we’re ready to populate those attributes¬†:

@override
protected void finishedLoading() {
    super.finishedLoading();
    insertProc = (String) getProperty("InsertProc");
    updateProc = (String) getProperty("UpdateProc");
    deleteProc = (String) getProperty("DeleteProc");
    insertParams = getParams("InsertIndex");
    updateParams = getParams("UpdateIndex");
    deleteParams = getParams("DeleteIndex");
}

This method uses two other methods:

  • EntityDefImpl.getProperty() – This returns a custom property of the entity object definition. For example, if you give an entity object definition a custom property called InsertProc, with the value “MY_PL_SQL_PACKAGE.INSERT_EMPLOYEE”, getProperty("InsertProc") will return “MY_PL_SQL_PACKAGE.INSERT_EMPLOYEE”. That’s one problem already solved–your base entity object class can figure out the package-qualified name of each appropriate procedure for each individual entity object definition.
  • getParams() – This method is supposed to return a list of attributes, in order, to plug in as parameters for each stored procedure. We’ll work on that next.

Stored Procedure Parameters

So let’s write that getParams() method:

private List<AttributeDef> getParams(String indexPropertyName) {
    AttributeDef[] workspace = new AttributeDef[getAttributeCount()];
    AttributeDef[] attrs = getAttributeDefs();
    int numParams = 0;
    for (AttributeDef attr : attrs) {
        String indexStr = (String) attr.getProperty(indexPropertyName);
        if (indexStr != null && indexStr.length() != 0) {
            int index = Integer.valueOf(indexStr);
            workspace[index - 1] = attr;
            if (index > numParams) numParams = index;
        }
    }
    return Arrays.asList(workspace).subList(0,numParams);
}

So, let’s say we pass the value “InsertIndex” into this method (which the constructor does). First, the method creates a temporary “workspace” array, big enough to hold all the attributes in the entity object definition. Then, it cycles through the attributes in the entity object definition, checking each one for an “InsertIndex” custom property, which is supposed to be an integer (1 or greater), and pops that attribute into the appropriate place (one index number smaller, to account for the 1-based/0-based mismatch in PL/SQL vs. Java) in the workspace array. It also keeps track of how much of the workspace array is actually being used. Finally, it converts the used part of the workspace array to a List, and returns it.

So, if you have an entity object definition, and you’ve given five of its attributes an “InsertIndex” property, from 1 to 5, this method will return a list, in order, of those attributes. The entity object class can use those to determine which values to plug in for parameters to the package procedure.

So What Does All This Buy Us?

So now, we can set custom properties on an entity object definition and its entity attributes to specify the name of the procedures to be used for insert, update, and delete and the number and order of attributes whose values should be passed to the procedure. As I’m sure you can imagine, it’s not that difficult to use this information to actually construct and execute the appropriate JDBC statements to perform the procedures. And what do we have?

  • Two Java classes, that can be reused across all your applications.
  • A need to set custom properties–which can be done declaratively in the Entity Object Editor–to specify package procedure details for each entity object definition that needs to use a package API instead of DML.

That means that, once you’ve written these two Java classes, you can create entity object definitions that use stored procedures to perform DML with no Java code. Heck, you could pass that task on to someone on your team who doesn’t even know Java. And that, my friends, is the power of properties.