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

1

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

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

that in combination with the standard configuration entry

  &lt;phase-listener&gt;oracle.jheadstart.controller.jsf.lifecycle.JhsADFPhaseListener&lt;/phase-listener&gt;<br />&nbsp;&nbsp;&nbsp; 

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()<br />  {<br />    return new nl.amis.adf.controller.faces.lifecycle.OurExtendedADFPageLifeCycle();<br />  }<br />  <br />

and register it ourselves in faces-config.xml:

  &lt;phase-listener&gt;nl.amis.adf.controller.faces.lifecycle..OurADFPhaseListener&lt;/phase-listener&gt;<br /><br />

 

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

      BindingContext bindingContext = (BindingContext)JsfUtils.getExpressionValue(&quot;#{data}&quot;);<br />      DCBindingContainer  container = bcx.findBindingContainer(&quot;GenericServicesPageDef&quot;);<br />      container.refresh(DCBindingContainer.PREPARE_MODEL);<br />&nbsp;

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

      BindingContext bcx = lfContext.getBindingContext();<br />&nbsp;

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

     DCBindingContainer  bindings = (DCBindingContainer)JsfUtils.getExpressionValue(&quot;#{bindings}&quot;);<br />     BindingContext bcx = bindings.getBindingContext();<br />&nbsp;

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

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

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:

    &lt;managed-bean&gt;<br />        &lt;managed-bean-name&gt;genericBindings&lt;/managed-bean-name&gt;<br />        &lt;managed-bean-class&gt;oracle.adf.model.binding.DCBindingContainer&lt;/managed-bean-class&gt;<br />        &lt;managed-bean-scope&gt;request&lt;/managed-bean-scope&gt;<br />    &lt;/managed-bean&gt;<br /><br />

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

 

Add the ViewObject to the GenericService ApplicationModule.

Add SysdateView.Sysdate to GenericServices.jspx (or rather to the GenericServicesPagteDef.xml file):

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

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:     

 

&lt;af:outputText value=&quot;#{genericBindings.SysdateViewSysdate.inputValue}&quot; /&gt;<br />

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):

and this other page in the same application:

 

 

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

1 Comment

  1. This article explains a function that would be extremely useful to me. But I’m new to ADF/JSF and having some trouble implementing it in an application that does not use JHeadstart, just plain ADF Faces.

    I created a FacesPageLifecycle and PhaseListener extension classes, registered the new PhaseListener in the Life Cycle section of faces-config.xml, made a genericBindings managed bean of class oracle.adf.model.binding.DCBindingContainer, overrode the prepareModel method of the FacesPageLifecycle extension class to initialize the container, and created a new Application Module exposing a SysdateView1 data control. No data is being returned from the example Sysdate data control.

    I noticed my Page Definition had an ID of “WEB_INF_GenericServicesPageDef” instead of “GenericServicesPageDef” as in your article, so I changed my prepareModel() override to use the name I found, but it made no difference. Your description of initializing the generic BindingContainer was confusing to me and I didn’t know what applied and what didn’t. I used the prepareModel() method exactly as listed in the article. Using “#{genericBindings.SysdateView1Sysdate.inputValue}” from my JSP page – it’s simply “#{bindings.SysdateView1Sysdate.inputValue}” on my GenericServices.jsp page – does not display any value.

    I tried running the debugger, but my customized FacesPageLifecycle extension class doesn’t seem to be called.

    Can you make any suggestions as to how I can tell what’s going wrong and how to fix it?