Adding a Method Call Activitiy to an ADF task flow
New Oracle ADF tutorial: How to add a Method Call Activitiy to a task flow. Also describes the manual approach, to get a better understanding of what happens in the background.
New Oracle ADF tutorial: How to add a Method Call Activitiy to a task flow. Also describes the manual approach, to get a better understanding of what happens in the background.
To get a better understanding how the Application Module Pool works in Oracle ADF, it can be useful to log some of its methods as they are called. This can also be useful to track down Application Module Pool related issues, or even to collect usage and performance statistics. The entry point for such a logging facility is to define a custom Application Module Pool class. Note that this is not something you would or should do in the final application – it is usually not necessary, but can be convenient for the use cases mentioned above. The class to instantiate as the Application Module Pool is defined through the PoolClassName property on the Application Module’s configuration:
The configuration is stored in the bc4j.xml configuration file, and we could also set the property by simply editing this file:
<appmoduleconfig deployplatform="LOCAL" jdbcname="ApplicationDB" jbo.project="model.Model" name="AppModuleLocal" applicationname="model.AppModule">
<am-pooling poolclassname="test.CustomApplicationModulePool">
<database jbo.locking.mode="optimistic">
<security appmodulejndiname="model.AppModule">
</security></database></am-pooling></appmoduleconfig>
Now, the ADF runtime will instantiate an object of the defined class as the Application Module’s pool. We simply need to implement this class, inheriting from ApplicationPoolImpl:
package test; import java.util.Properties; import oracle.jbo.ApplicationModule; import oracle.jbo.common.ampool.ApplicationPoolImpl; import oracle.jbo.common.ampool.SessionCookie; public class CustomApplicationModulePool extends ApplicationPoolImpl { public CustomApplicationModulePool() { super(); } ... }
By overriding the methods which are of interest, we can add logging to the application module pool and for example track how Application Modules are checked out and released. Ideally a logging API such as java.util.logging should be used – for simplicity, I am using System.err in the examples. The most interesting methods to track Application Module usage are useApplicationModule() which is called by the framework to check out an application module from the pool, releaseApplicationModule() which is called to give back an application module to the pool and probably createSessionCookie() which creates a session cookie for a BC4J session:
@Override public SessionCookie createSessionCookie(String applicationId, String sessionId, Properties properties) { System.err.printf("createSessionCookie(applicationId=%s, sessionId=%s, properties=%s)\n", applicationId, sessionId, properties); SessionCookie result = super.createSessionCookie(applicationId, sessionId, properties); System.err.printf(" Result: %s\n", result.getSessionId()); return result; } @Override public ApplicationModule useApplicationModule(final SessionCookie cookie, final boolean checkout) { System.err.printf("useApplicationModule(session id=%s, checkout=%s)\n", cookie.getSessionId(), checkout); ApplicationModule result = super.useApplicationModule(cookie, checkout); System.err.printf(" Result: %s\n", System.identityHashCode(result)); return result; } @Override public void releaseApplicationModule(SessionCookie cookie, boolean manageState) { System.err.printf("releaseApplicationModule(cookie=%s, manageState=%s)\n", cookie, manageState); super.releaseApplicationModule(cookie, manageState); } @Override public void releaseApplicationModule(SessionCookie cookie, int releaseFlags) { System.err.printf("releaseApplicationModule(cookie=%s, releaseFlags=%s)\n", cookie, releaseFlags); super.releaseApplicationModule(cookie, releaseFlags); }
We can now observe how and when the ADF runtime retrieves Application Modules form the pool. Remember that each HTTP request which requires BC4J will result in an Application Module checkout and a corresponding release before the request finishes. Also remember that each Application Module has its own database transaction (unless it is a nested Application Module, in which case it inherits the transaction from its parent). Hence, logging information about the associated database transaction can also be useful.
In larger ADF projects with more than one workspace and with many projects, it might happen that a breakpoint can not be set on a class from a different workspace or project, even though JDeveloper properly navigates to the source file. In this case, make sure that the “Scope for New Breakpoints” is set to “Global” in the “Debugger/Breakpoints” preferences (open the preferences dialog through “Tools/Preferences”):
In Oracle ADF, each definition object (those objects which are used as the templates for actual objects) has a scope which can be either session based or application based (shared between sessions). The following diagram shows a rough (and probably somewhat incomplete) overview of the runtime structure of some of the most common objects (Entity object, View object, Application Module) and their corresponding definition objects:
Essentially, there are two possible ways how to create a definition object:
When a new definition object has been created, it should be registered with the meta object manager so that it can be looked up by its name later. This is done by calling the registerDefObject() method on the definition object:
ViewDefImpl mydef = new ViewDefImpl("view.DataVO"); ... mydef.registerDefObject();
The meta object manager is an application wide singleton, and it has a method dumpMOM() which can be used to dump the definition objects which are currently registered:
MetaObjectManager mom = MetaObjectManager.getSingleton(); mom.dumpMOM(new PrintWriter(System.err), true);
<< Shared DefinitionContext >> -- oracle.jbo.mom.DefinitionContextAgeable.dumpMOM -- ... << Session DefinitionContext >> -- oracle.jbo.mom.DefinitionContextAgeable.dumpMOM -- ...
Note that the parameter-less dumpMOM() overload prints to System.out, which might overlap with other ad-hoc debug output on System.err you might be using in your test application (you would never use System.out or System.err in production code anyway). Internally, the meta object manager uses entries in the ADFContext‘s applicationScope and sessionScope maps – so it is capable of managing session specific objects even though itself it is an application singleton.
When modifying definition objects at runtime, make sure that you are not modifying application scoped definition objects (unless your intention is that all objects in all sessions derived afterwards will inherit this modification). Instead, programmatically create a session scoped definition object, or use the instance specific methods for the modifications. For example, instead of modifying or creating a new ViewDefImpl object with a specific query string and then create a ViewObjectImpl based on it, it is also possible to set the query string directly on the ViewObjectImpl itself – it then only affects this particular view object instance.