Shared Application Module Instance Tricks, Part I: Service Methods

Last week, I talked a bit about the advantages of using shared application module instances, particularly those scoped at the application level. As I said there, creating shared application module instances is described in Chapter 10 of the Fusion Developer’s Guide for Oracle ADF, as is their primary use, containing view object instances for lookups, to be used in validation and LOV attributes.

This week and next, I’m going to talk about other things you can do with shared application module instances, things that aren’t as well documented. This week, I’ll talk about calling service methods on shared application module instances, the view object instances in their data model, or particular rows from thise view object instances; next week, I’ll talk about actually displaying data from the shared instance’s data model as components (other than choice/LOV components) on a page.

Calling Application Module Service Methods on a Shared Application Module Instance

If you want to provide a coarse-grained data service from a regular application module instance, you write a method on an application module class, view object class, or view row class, expose it on the appropriate client interface, and drag it (or its return value or components) from the data control palette onto a JSF page/fragment or a task flow, as appropriate.

I want to stress that you can’t do this to access a coarse-grained service on a shared application module instance. Oh, sure, a data control for the application module will appear in the data control palette, with all its view objects below it, but dragging will not create components based on the shared instance–it will create an ordinary, top-level instance of the application module (which is not shared across users) and create components based on that.

To use shared application module instance methods (or view object or view row methods) in the view or controller layer, you need to create client methods on your regular application module (not the shared one) that delegate to the shared application module instance.

How do you do that? Well, I’ll show you. Here’s the example–just based on HR, for simplicity. In this example, I have two application module definitions: TopLevelModule (which is going to be used to create ordinary top-level application module instances, one per user session, modulo pooling) and one called SharedModule, which is going to be used exclusively as to create an application-scoped shared application module instance. I’ve added that instance, called SharedModuleInstance, to the project properties (you don’t actually need to do that to call service methods, but it will be useful for the other techniques later).

I want to provide some coarse-grained data service on SharedModule with the following declaration (the implementation doesn’t really matter for our purposes):

public int sharedMethod(List<Number> param1, String param2)

I write the method on SharedModuleImpl (the application module class for SharedModule). I don’t bother adding this to a client interface; the client is never going to call it directly. Instead, I’m going to write a pair of methods in TopLevelModule:

private SharedModuleImpl getSharedModuleInstance() {
    return (SharedModuleImpl) findOrCreateSharedApplicationModule("SharedModuleInstance",
                                                                  "sharedam.model.SharedModule",
                                                                  SHARED_SCOPE_APPLICATION);
}
pubilc int sharedMethod(List<Number> param1, String param2) {
    return getSharedModuleInstance().sharedMethod(param1, param2);
}

getSharedModuleInstance() is a convenience method, just because that one line is going to be quite a fingerfull to type repeatedly if we need to expose more than one shared method. It calls the rarely-mentioned method findOrCreateSharedApplicationModule(), which takes three arguments:

  • The name of the shared instance
  • The package-qualified name of the shared instance’s definition
  • A byte, which should be either the constant ApplicationModuleImpl.SHARED_SCOPE_APPLICATION or ApplicationModuleImpl.SHARED_SCOPE_SESSION.

If we hadn’t already specified the shared instance in the project properties, this method would create it for us the first time it was called (which is why, to call service methods, you don’t really need to add the instance in the project properties), but on subsequent calls (or on the first call for instances that are in project properties), it returns the same instance. You just want to make sure that all three parameters match the details for the existing shared instance.

And the method sharedMethod() is even simpler. It’s a bare delegator to the method of the same name on the shared application module instance. This is the method you can expose on the client interface (for TopLevelApplicationModule) and invoke from the client. The shared application module instance will perform all the work.

A Warning: Many service methods aren’t really for retrieving data, but for making a change either to data or to the object they’re called on (e.g., navigating the primary row set iterator through a view object’s rowset). I do not recommend writing such methods for use with application-scoped shared application module instances. Remember, such an instance is shared accross user sessions, and changing it is likely to cause lots of confusion or errors for other users who find that stuff has happened between their requests. Try not to have any side effects in your method–use secondary row set iterators that you close by the end of the method, etc.

Calling View Object Service Methods on a Shared Application Module Instance’s View Object Instances

What about service methods on view object instances inside the shared application module instance’s data model? How can you call them? It’s actually pretty simple if you’ve learned how to do the above. To demonstrate, let’s give SharedModule’s data model an instance of DepartmentsView (the default view object for the DEPARTMENTS table), called AllDepartments, and write a method with the following signature on DepartmentsiewImpl:

public String sharedViewMethod(String input)

Again, no need to export this method either. Instead, we’ll (again) write a delegator method on TopLevelModuleImpl:

public String sharedViewMethod(String input) {
    return getSharedModuleInstance().getAllDepartments().sharedViewMethod(input);
}

And again, export this method and call it from the client. Pretty easy, huh?

Of course, the same issues apply as before: Don’t let the service method have side effects. Other people have to use this view object instance too–which is kind of the point.

Calling View Row Service Methods on a Shared Application Module Instance’s View Rows

By now, the general idea of calling service methods on a shared application module instance or its data model elements is probably pretty obvious. Create a (not necessarily exported) method on the class you want, and invoke it from your regular, top-level application module instance by delegation. But there’s an extra little wrinkle in doing this with view-row level methods: How are you going to get a handle on the view row instance?

To illustrate the problem, I want to give you an example of something that won’t work, or at least, is a very bad idea:

public int dontExecuteAViewRowMethodThisWay(boolean param) {
    public DepartmentsViewRowImpl firstDepartment = (DepartmentsViewRowImpl)
        getSharedModuleInstance().getAllDepartments().first();
    return firstDepartment.sharedViewRowMethod(param);
}

The problem with that is the call to first(). That disturbs the default row set iterator, setting it to the first row, which may be a problem if other people are using the view object instance as well. Here’s a much better way to go about it:

 public int sharedViewRowMethod(boolean param) {
    public RowSetIterator tempDepartmentsIterator =
        getSharedModuleInstance.getAllDepartmentss.createRowSetIterator(null);
    public DepartmentsViewRowImpl firstDepartment = (DepartmentsViewRowImpl) tempDepartmentsIterator.first();
    tempDepartmentsIterator.closeRowSetIterator();
    return firstDepartment.sharedViewRowMethod(param);
}

It’s the same issue as before: You don’t want to touch the primary row set iterator (or any of the data) in a shared application module instance. There are other ways to get a particular row too:

  • Use ViewObject.findByKey() or ViewObject.findByAltKey(), which return an array of rows, or a row iterator through which you can navigate, respectively, without disturbing the primary row set iterator.
  • Use the rowsets returned by view accessors, which can be safely navigated through without disturbing the primary row set iterator. We’ll talk about using these rowsets more next week.

More stuff then!