Provide your ADF Faces application with a central BindingContainer for generic access to application wide services

Some operations and data, available from one or more ADF Data Controls, are useful across the application. From just abaout anywhere in the ADF Faces Web Application, you may want access to such operations and data. Typically, we work in the context of a JSF page that has an associated PageDefinition (ir BindingContainer at run time) and we have all bindings in the BindingContainer at our run time disposal. One way of making services available at any time in any situation any state in the application, is adding bindings for those services to every PageDefinition. It is not attractive: adding one or more bindings to all PageDefinitions is a lot of work, especially when the definition of those bindings has to change or one is added or removed.

We are looking for a way to bring all generic bindings in a single PageDefinition that we can use across the application. And it turns out to be quite simple to do so.

The steps:
1. create a new JSF page called something like GenericServices.jspx
2. drag all services we require to the page, dropping them as whatever we like. Remember: all we care about is the PageDefinition we are thus creating, not the page itself. Note that any refining of the service bindings can be done in the GenericServicesPageDef.xml file

Now the BindingContainer is available in theory. However, ....
it has to be initialized. ADF will initialize all BindingContainers for the normal JSF pages that are accessed. We have to take control over initializing our GenericService BindingContainer as it is not directly tied to any page. There are several strategies for this:

– add a ServletFilter to act right after the ADFBindingFilter
– extend the FacesPageLifecycle and override the prepareModel phase to initialize the generic binding container
– add a JSF PhaseListener

There are some differences between these methods when it comes to the exact moment of initializing the binding container. Let’s just try one: extending the PageLifeCycle.

3. Create a class (OurExtendedADFPhaseListener) that extends oracle.adf.controller.faces.lifecycle.FacesPageLifecycle (or, when you are using JHeadstart’s runtime infrastructure, oracle.jheadstart.controller.jsf.lifecycle.JhsPageLifecycle). In the class, override the prepareModel() method. Have it call the super.prepareModel() and then initialize our special BindingContainer.

4. Register the extended LifeCycleClass:

When using JHeadstart: simply register the new PageLifeCycle in faces-config.xml

  <managed-bean>
<managed-bean-name>jhsPageLifecycle</managed-bean-name>
<managed-bean-class>nl.amis.adf.controller.faces.lifecycle.OurExtendedADFPageLifeCycle</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
:

that in combination with the standard configuration entry

  <phase-listener>oracle.jheadstart.controller.jsf.lifecycle.JhsADFPhaseListener</phase-listener>
   

takes care of putting the desired lifecycle class in place.

for plain ADF Faces applications, we also need to override the ADFPhaseListener and have it override createPageLifecycle()

 

protected PageLifecycle createPageLifecycle()
{
return new nl.amis.adf.controller.faces.lifecycle.OurExtendedADFPageLifeCycle();
}

and register it ourselves in faces-config.xml:

  <phase-listener>nl.amis.adf.controller.faces.lifecycle..OurADFPhaseListener</phase-listener>

 

The code for initializing our central generic BindingContainer is something like this:

      BindingContext bindingContext = (BindingContext)JsfUtils.getExpressionValue("#{data}");
DCBindingContainer container = bcx.findBindingContainer("GenericServicesPageDef");
container.refresh(DCBindingContainer.PREPARE_MODEL);
 

Note: in the PageLifeCycle class we can also get to the BindingContext from the LifeCycle Context:

      BindingContext bcx = lfContext.getBindingContext();
 

if we have access to a live bindingContainer, we can also get the BindingContext from there:

     DCBindingContainer  bindings = (DCBindingContainer)JsfUtils.getExpressionValue("#{bindings}");
BindingContext bcx = bindings.getBindingContext();
 

In this case, I am using JHeadstart and simply extending the JhsPageLifecycle class. My class looks like this:

public class OurExtendedADFPageLifeCycle extends JhsPageLifecycle {
public OurExtendedADFPageLifeCycle() {
}


public void prepareModel(LifecycleContext lfContext) {
super.prepareModel(lfContext);
BindingContext bcx = lfContext.getBindingContext();
DCBindingContainer container = bcx.findBindingContainer("GenericServicesPageDef");
container.refresh(DCBindingContainer.PREPARE_MODEL);
// now we have to make our BindingContainer available to the rest of the world
// we set the genericBindinga managed bean to our initialized bindingContainer
ValueBinding genericBindings = FacesContext.getCurrentInstance().getApplication().createValueBinding("#{genericBindings}");
genericBindings.setValue(FacesContext.getCurrentInstance(), container);
}
}
 

Note how I have set the managed bean reference #{genericBindings} to refer to the initialized GenericServices binding container. This managed bean has been configured in faces-config.xml as:

    <managed-bean>
<managed-bean-name>genericBindings</managed-bean-name>
<managed-bean-class>oracle.adf.model.binding.DCBindingContainer</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>

Example of using the GenericServicesBindingContainer – application wide access to SYSDATE! 

Now for one of the simplest and most useless examples of leveraring this generic services BindingContainer: have (the database server’s current date and time) sysdate available on all my pages!

1. Create view object SysdateView, based on the query select sysdate from dual

Provide your ADF Faces application with a central BindingContainer for generic access to application wide services genericpagedef001 

Add the ViewObject to the GenericService ApplicationModule.

Add SysdateView.Sysdate to GenericServices.jspx (or rather to the GenericServicesPagteDef.xml file):
Provide your ADF Faces application with a central BindingContainer for generic access to application wide services genericpagedef002

Note: what this is about is not the JSPX page – that will never be displayed – but the underlying PageDefinition that is created/extended.

Provide your ADF Faces application with a central BindingContainer for generic access to application wide services genericpagedef003

We could also just have added the iterator and attributeValues definition to the pageDef without bothering with the JSPX.

We can now, in any page in the application, refer to the Sysdate through #{genericBindings.SysdateViewSysdate.inputValue}, such as:     

 

<af:outputText value="#{genericBindings.SysdateViewSysdate.inputValue}" />

It is now easy to add the sysdate to for example a global region that is included in every page in the application, without having to add the SysdateView to the PageDefinitions of all those pages.

Notice the subt
le date reference in the upper right
hand corner (note: displaying the date in web application is not commonly done using SYSDATE as source of that date):

Provide your ADF Faces application with a central BindingContainer for generic access to application wide services centralbindingcontainer001

and this other page in the same application:

Provide your ADF Faces application with a central BindingContainer for generic access to application wide services centralbindingcontainer002 

 

One Response

  1. Ed Eaglehouse August 1, 2008