Tuesday, April 13, 2010

Invoking ApplicationModule from a servlet using ADF Model

Sometimes you may need to invoke services exposed by an ApplicationModule from a non ADF web application. This blog post discusses an interesting approach for such use cases where the business logic is residing in a servlet and which in turn invokes the methods exposed through the ApplicationModule, during the course of execution.

One obvious solution is to create ApplicationModule on the fly and release it at the end of the request, as shown below. This code will work if your entire transaction gets committed within a single request. In other words, this is ideal for those use cases which does not call for maintaining state across requests from the same client.

try{
   ApplicationModule applicationModule = Configuration.createRootApplicationModule(qualifiedAMDefName, configName);
   //Business logic goes here.......
}catch(Excpetion ex){
   //Handle Error
}finally{
   Configuration.releaseRootApplicationModule(applicationModule, false);
}

Please note that you may need to configure the ServletADFFilter, to ensure the proper initialization of ADFContext while accessing ApplicationModule from a non ADF based servlet (as in the above use case scenario).
This filter reads the configuration from WEB-INF/adf-config.xml and initializes ADFContext. However applications using ADFBindingFilter can skip this filter, as this is already taken care by the binding filter.

What if you need to maintain the conversation state across multiple requests?

This is a bit tricky situation. As you might be updating Entity Objects from separate requests and committing the transaction at the end of the conversation, it's important to pass a session identifier while looking up the ApplicationModule to restore the state. There are set of APIs available which may help you to pass session cookie while creating ApplicationModule. You may need to follow certain system level contracts and APIs to create a 'stateful' ApplicationModule. Needless to say, this approach call for good understanding of the execution path of the binding layer and related APIs to make it working. So let me try explaining yet another interesting approach which may free you from all these hazards.

Using binding layer to access ApplicationModule

Idea is to make use of the binding layer to invoke services(methods) defined at the ApplicationModule. Apparently you may need same set of meta files as used by an ADF Fusion Web application, to automate this process. If you build an ADF Face application, these files are generated by the JDeveloper at the design time. In this specific case, you may need to create them manually(or to avoid the manual error, you may create a dummy project with a page and necessary ApplicationModule method bindings, and copy the generated adfmsrc folder containing the meta files to your Web project).


Now, let us take a step back and see who is responsible for wiring ApplicationModule with view layer in the case of a typical Fusion web application. Fusion applications are configured to use a servlet filter ADFBindingFilter which actually orchestrates the automatic acquisition and release of an
appropriate application module instance based on declarative binding meta-data.
The method ADFBindingFilter.doFilter() sets up the ADF processing state, and the method ADFBindingFilter.initializeBindingContext() creates an instance of oracle.adf.model.BindingContext by reading the CpxFileName init parameter from the web.xml file. So, next step is configuring this binding filter. You are free to subclass ADFBindingFilter, if you want to customize the run time behavior of this binding guy. Add this to web.xml with proper url-pattern, so that let the run time pick up this filter and execute it before running your page/servlet.


<filter>
  <filter-name>CustomBindingFilter</filter-name>
  <filter-class>web.filter.CustomBindingFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>CustomBindingFilter</filter-name>
  <url-pattern>/adf/*</url-pattern>
</filter-mapping>

Servlet code that access ApplicationModule may look like as shown below

BindingContext bindingContext=null;
try {
    bindingContext = BindingContext.getCurrent();
    DCBindingContainer dcBindingContainer =
    bindingContext.findBindingContainer("view_dummyPageDef");
    JUCtrlRangeBinding depts =
    (JUCtrlRangeBinding)dcBindingContainer.findCtrlBinding("DepartmentsView1");
    dcBindingContainer.refreshControl();
    // Business logic goes here...
 }finally {
   // Clean up goes here...
}

Now you can try running your servlet. Please make sure that servlet URL contains the patterns that you configured for the binding filter. For example, please see '/adf/' appearing in the below URL, which result in interception of the binding filter before invoking the servlet.
http://127.0.0.1:7101/AMWithServlet-UI-context-root/adf/sampleservlet

You can download the sample workspace from here. Unzip the project and run the SampleServlet.java

[Runs with Oracle JDeveloper 11g R1 PS1 + HR Schema]

This article inspired by an example from Steve Muench

Wednesday, April 7, 2010

Custom <af:train> model

The <af:train> component presents a series of 'stops', each representing a task in a multi-step process, carried out sequentially. This component is very useful when you need to develop a 'wizard' sort of screens.


In general, af:train is used along with taskflow where the implementation part is a cakewalk (can be done declaratively with zero effort). Sometimes you may need to customize the default behavior of the train component. ADF Faces does support this customization through the custom menu model. To learn more about this, please go through chapter 18.7 Using Train Components to Create Navigation Items for a Multi-Step Process of Web User Interface Developer's Guide.

I'm attaching a simple af:train demo using customized org.apache.myfaces.trinidad.model.ProcessMenuModel as model. This is actually a simplified version of the af:train component sample, bundled with ADF Faces Rich Client Components Demo.

A brief description of this implementation...

jspx tag snippet is shown below

 <af:train var="node" value="#{viewScope.CustomTrainModel}"
                        id="train">
   <f:facet name="nodeStamp">
     <af:commandNavigationItem text="#{node.label}"
            actionListener="#{node.doActionListener}"
            immediate="#{viewScope.CustomTrainModel.immediate}"
            disabled="#{viewScope.CustomTrainModel.readOnly}"
            visited="#{viewScope.CustomTrainModel.visited}"
            id="commandNavigationItem"/>
  </f:facet>
</af:train>

Class diagram for custom menu model


Custom menu model used for building this sample is TrainIdMenuModel class. This is subclassed from org.apache.myfaces.trinidad.model.ProcessMenuModel. Initialization logic is copied below for your reference. Please note the usage of @PostConstruct in this bean to perform the initialization.

@PostConstruct: Specifies a method that the container will invoke after resource injection is complete but before any of the component's life-cycle methods are called.

    @PostConstruct
    public void initilize() {
        /**
         * Gets the nodes for the train
         */
        List<NavigationItemId> viewEntries =
            ServiceProxy.getInsatnce().getSomeDummyNavigationItemsToTest();

        ChildPropertyTreeModel childPropertyTreeModel =
            new ChildPropertyTreeModel();
        childPropertyTreeModel.setChildProperty("children");
        childPropertyTreeModel.setWrappedData(viewEntries);

        this.setViewIdProperty("id");
        this.setWrappedData(childPropertyTreeModel);
        this.setIdKey("idTrainViewIdKey");
    }

You can download the af:train sample using custom menu model from here.

Adding listeners for each train stop

Sometimes, you may need to invoke custom actions while navigating through train stops. The below sample provides an interesting approach for this requirement. Idea is to enhance NavigationItemId's doActionListener() method, to trigger the registered action listeners. Below sample implements this use case(Please note that, this sample is just intended to give you an idea on this topic).

You can download the af:train sample using custom menu model+custom action from here.