Adaptive Connections For ADFBC
- by Duncan Mills
Some time ago I wrote an article on Adaptive Bindings showing how the pageDef for a an ADF UI does not have to be wedded to a fixed data control or collection / View Object. This article has proved pretty popular, so as a follow up I wanted to cover another "Adaptive" feature of your ADF applications, the ability to make multiple different connections from an Application Module, at runtime.
Now, I'm sure you'll be aware that if you define your application to use a data-source rather than a hard-coded JDBC connection string, then you have the ability to change the target of that data-source after deployment to point to a different database. So that's great, but the reality of that is that this single connection is effectively fixed within the application right? Well no, this it turns out is a common misconception.
To be clear, yes a single instance of an ADF Application Module is associated with a single connection but there is nothing to stop you from creating multiple instances of the same Application Module within the application, all pointing at different connections. If fact this has been possible for a long time using a custom extension point with code that which extends oracle.jbo.http.HttpSessionCookieFactory. This approach, however, involves writing code and no-one likes to write any more code than they need to, so, is there an easier way? Yes indeed. It is in fact a little publicized feature that's available in all versions of 11g, the ELEnvInfoProvider.
What Does it Do?
The ELEnvInfoProvider is a pre-existing class (the full path is oracle.jbo.client.ELEnvInfoProvider) which you can plug into your ApplicationModule configuration using the jbo.envinfoprovider property. Visuallty you can set this in the editor, or you can also set it directly in the bc4j.xcfg (see below for an example) .
Once you have plugged in this envinfoprovider, here's the fun bit, rather than defining the hard-coded name of a datasource instead you can plug in a EL expression for the connection to use. So what's the benefit of that? Well it allows you to defer the selection of a connection until the point in time that you instantiate the AM.
To define the expression itself you'll need to do a couple of things:
First of all you'll need a managed bean of some sort – e.g. a sessionScoped bean defined in your ViewController project. This will need a getter method that returns the name of the connection. Now this connection itself needs to be defined in your Application Server, and can be managed through Enterprise Manager, WLST or through MBeans. (You may need to read the documentation [http://docs.oracle.com/cd/E28280_01/web.1111/b31974/deployment_topics.htm#CHDJGBDD] here on how to configure connections at runtime if you're not familiar with this)
The EL expression (e.g. ${connectionManager.connection} is then defined in the configuration by editing the bc4j.xcfg file (there is a hyperlink directly to this file on the configuration editing screen in the Application Module editor). You simply replace the hardcoded JDBCName value with the expression. So your cfg file would end up looking something like this (notice the reference to the ELEnvInfoProvider that I talked about earlier)
<BC4JConfig version="11.1" xmlns="http://xmlns.oracle.com/bc4j/configuration">
<AppModuleConfigBag ApplicationName="oracle.demo.model.TargetAppModule">
<AppModuleConfig DeployPlatform="LOCAL"
JDBCName="${connectionManager.connection}"
jbo.project="oracle.demo.model.Model"
name="TargetAppModuleLocal"
ApplicationName="oracle.demo.model.TargetAppModule">
<AM-Pooling jbo.doconnectionpooling="true"/>
<Database jbo.locking.mode="optimistic">
<Security AppModuleJndiName="oracle.demo.model.TargetAppModule"/>
<Custom jbo.envinfoprovider="oracle.jbo.client.ELEnvInfoProvider"/>
</AppModuleConfig>
</AppModuleConfigBag>
</BC4JConfig>
Still Don't Quite Get It?
So far you might be thinking, well that's fine but what difference does it make if the connection is resolved "just in time" rather than up front and changed as required through Enterprise Manager?
Well a trivial example would be where you have a single application deployed to your application server, but for different users you want to connect to different databases. Because, the evaluation of the connection is deferred until you first reference the AM you have a decision point that can take the user identity into account.
However, think about it for a second. Under what circumstances does a new AM get instantiated? Well at the first reference of the AM within the application yes, but also whenever a Task Flow is entered - if the data control scope for the Task Flow is ISOLATED. So the reality is, that on a single screen you can embed multiple Task Flows, all of which are pointing at different database connections concurrently.
Hopefully you'll find this feature useful, let me know...