Tuesday, November 26, 2013

Testing AM Activation and Passivation

The Application Module activation and passivation phenomenon can't be practically tested with default AM parameters as shown below. This is because the passivation happen only when a fresh AM instance can't be acquired from the pool. The max pool size is 4096 by default.

We can set the parameters to certain values which will enforce the passivation to happen every time a fresh instance of AM is required. The activation will happen when the client which was using the AM instance that got passivated comes back with a request to access AM. 



  Alternatively we can disable application module pooling in which case, regardless of the pool sizes passivation and activation happens everytime an AM instance is requested.


If a user wants passivation to happen everytime an AM instance state is changed, so that in case we have our Application Server in failover mode, we can achieve that by setting jbo.dofailover = true.


Wednesday, November 13, 2013

Implementing Select All in adf table getting the data from a Pojo datacontrol

There is a af table where one of the column has checkbox that enables user to select or deselect. There is no row selection in that table and hence selection of checkbox is  treated as selection of row.

There is checkbox in the column header of that column and on checking that checkbox, I want all the checkboxes in the respective column to be selected and viceversa. This can be achieved by following solution approach.

The table is getting populated by bindings corresponding to a collection in a POJO data control. Below is the code snippet for Pojo DC. The Pojo is chosen here instead of ADF ViewObject because the list of employees is obtained from Session here.

public class myPojoDc{
       private List<Employee> employeeList;

       public List<Employee> getEmployeeList(){
             if(employeeList == null) {
                 prepareEmployeeList();
             }
             return employeeList;
       }

       private void prepareEmployeeList(){
          //Access the list from Session and set it to employeeList.
       }
}

This data control is looked up and the employeeList is dragged and dropped on a ADF jsff form. This creates bindings in pagedef and af:table in the jsff.

Below is the code snippet for value change event handler of selectAll checkbox in the af:table.


    public void handleAccountsSelectAll(ValueChangeEvent ve) {
     
        BindingContainer bindings =
            BindingContext.getCurrent().getCurrentBindingsEntry();
        DCIteratorBinding it =
            (DCIteratorBinding)bindings.get("employeeListIterator");

        RowSetIterator rit = it.getRowSetIterator();
        rit.reset();
        int count = 0;
        if (selectAll != null) {
            if (rit.first() != null) {
                Row r = rit.first();
                r.setAttribute("selected", selectAll);
                if (selectAll) {
                    count++;
                }
            }
            while (rit.hasNext()) {
                Row r = rit.next();
                if (r != null) {
                    r.setAttribute("selected", selectAll);
                    if (selectAll) {
                        count++;
                    }
                }
            }
        }
        //The count here represents the total count selected rows which is nothing but all rows. The count is kept in pageFlowScope so that is used by outputtext located in below to af:table in the jsff.               
        getPageFlowScope().put(respectiveCount, count);
        it.executeQuery();
        //table instance below is obtained by component binding to af:table.
        addPartialTarget(table);
    }

Monday, October 28, 2013

Showing Modified rows in a table using filtering feature

I came across a requirement where a table having a list of employee entities. The table is not a updatable table. So each row is modified by a form that comes up in a dialog on selecting a row. A new row is created on clicking + icon in the table toolbar. This table has filtering enabled. I have a flag "Show only to be submitted entities". On selecting the checkbox corresponding to that flag, the table should show only the newly added or modified rows.

To achieve this, I followed a simple approach of using filtering. Firstly I maintained a transient attribute in the entity which is a plain string and is updated on every new row creation or modification. I call that attribute a marker and the values are (N) for newly newly added ones and (M) for modified ones. On clicking the "Show only to be submitted entities"checkbox, I am populating a filter criteria saying marker startswith "(". Then queued the queryevent. This table has filtering enabled. So I also ensured that the existing filters are not disturbed. The "showOnlyToSubmitEntities" is boolean flag that preserves the selection of "Show only to be submitted entities" checkbox. If it is selected, the filter criteria is applied with marker added to it. Else it is removed.

Queuing event on selecting checkbox: (getTable() method returns the binding of the RichTable)

                  FilterableQueryDescriptor queryDescriptor =
                        (FilterableQueryDescriptor)getTable().getFilterModel();
                  getTable().queueEvent(new QueryEvent(getTable(), queryDescriptor));
----------------------------------------------------------------------------------------------------------------------

Handling the query event:

          FilterableQueryDescriptor fqd =
              (FilterableQueryDescriptor)queryEvent.getDescriptor();
          Iterator itr = null;

          if (showOnlyToSubmitEntities != null && showOnlyToSubmitEntities) {
              fqd.getFilterCriteria().put("marker", "(");
          } else {
              itr = fqd.getFilterCriteria().entrySet().iterator();
              while (itr.hasNext()) {
                  if (((Map.Entry)itr.next()).getKey().equals("legend")) {
                      itr.remove();
                  }
              }
          }

          ADFUtil.invokeEL(el, new Class[] { QueryEvent.class },
                           new Object[] { queryEvent });

Thursday, October 24, 2013

some best practices



The ADF Framework promotes the MVC pattern through the use of a well defined view, controller and model layers. The view layer is the JSPX fragments and associated backing bean code, the controller is the binding layer (ADFc) and the model is the business layer, typically ADF BC. A central tenet of the MVC pattern is that view and model communication is orchestrated by the controller layer. 

In some implementations, the backing bean code often directly invokes the Business tier directly without using the binding layer. The end result of this is a tight coupling between the view and model layer, where changes to the model layer could easily break the view layer code.Such implementation is also at risk since the benefits of the controller layer like contextual events, or deferred execution cannot now be performed. This also makes the code difficult to customize and change later.

Recommendation:
Re-factor the view layer code to use the binding layer. The view layer code should not directly invoke business methods, bypassing the ADF controller or the binding layer.

For example if you need to use any method define inside the applicationModule class, DON’T make a reference of the applicationModule in your code instead bind this method to the page and use the below code to access it:

         BindingContainer bindings =BindingContext.getCurrent().getCurrentBindingsEntry();
         OperationBinding submitTable = bindings.getOperationBinding("OperationName");
         submitTable.getParamsMap().put( "Parameters" , "data" );
         submitTable.execute();

---------------------------------


Best Practice for iterating over View Object rows
Create a secondary RowSetIterator if the business logic needs to iterate over rows of view. Note that the default RowSetIterator is typically bound to the UI components and use of it in
business logic will give inconsistent results at runtime. Call closeRowSetIterator() on the RowSetIterator instance once the business logic is done with the iteration. An example would be as follows:
public void checkAllPolicies() {
RowSetIterator policyRSI = null;
try {
ViewObject vo = findViewObject("PolicyVO");
vo.setNamedWhereClauseParam("policyNumber", getPolicyNumber());
vo.executeQuery();
//Create secondary row set iterator
policyRSI = vo.createRowSetIterator("customRowSet");
while (policyRSI.hasNext()) {
Row policyRow = policyRSI.next();
// -- business logic
}
} finally {
//Close the secondary row set iterator
if (policyRSI!= null)
policyRSI.closeRowSetIterator();
}
}


When iterating over view objects that are also used in the UI, the access mode can be set programmatically at runtime. You can restore the original access mode later based on the use case. The advantage of doing this programmatically is that you can use the forward-only mode only when needed, while other users of the view object, like the UI, will not be affected.
public void iteratePolicyVO() {
ViewObject vo = findViewObject("PolicyVO");
byte origMode = empVO.getAccessMode();
vo.setNamedWhereClauseParam("policyNumber", getPolicyNumber());
//Set access mode to FORWARD_ONLY
empVO.setAccessMode(ViewObject.FORWARD_ONLY);
vo.executeQuery();
while (vo.hasNext()) {
Row policyRow = vo.next();
// -- Business logic
}
//Restore the access mode
empVO.setAccessMode(origMode);
}
  ----------------------------------------------------------

Please make sure that you take care of the following in your code.

1      All the beans that you use in your code irrespective of the scope should implement searializable.
2      As a best practice for ADF, try as much as you can to not use binding for the UIcomponent and if you really need to use it, don’t use it in a memory scope more than Request or Back bean
scope. UIcomponent binding is only valid in request scope and backing bean scope. UIComponents are non-serializable, and therefore cannot be stored into any memory that would cause them to live longer than one request. If you need to keep a reference to a component longer than that you may use a component reference.

Please check the below link:

See “2.6.3 What You May Need to Know About Component Bindings and Managed Beans”

        Review your code and make sure you do the following:

1      Remove UIComponent Binding if there is a alternative way to do the same logic
2      If you must use it, make sure that you don’t use UIComponent bindings in beans that live longer than request scope and backing bean scope.
3      This is the last option. In case you have used component binding in beans with scope higher than request scope and backing bean scope, you can use  the component reference API to make the UI component also searializable.

Saturday, July 27, 2013

Overriding the default data source details for an Application Module @ run time

The Application module gets its respective data-source details from it currently active configuration. The configuration has two means of accessing database. One is the jdbc connection url and the other is jndi name of jdbc datasource. The jdbc connection url will be available in connections.xml of the respective application.

If we want AM to read the datasource string of our choice at runtime, we need to configure the AM property - " jbo.envinfoprovider". By default nothing is configured to it. we will need to implement our CustomEnvInfoProvider and configure it that property. Below is the snippet of the CustomEnvInfoProvider.
---------------------------------------------------------------------------------------------------
package model;

import java.io.FileInputStream;
import java.util.Hashtable;
import java.util.Properties;
import oracle.jbo.client.Configuration;
import oracle.jbo.common.ampool.EnvInfoProvider;

public class CustomEnvInfoProvider implements EnvInfoProvider {
    public CustomEnvInfoProvider() {
        super();
    }

    public Object getInfo(String infoType, Object env) {
        if (EnvInfoProvider.INFO_TYPE_JDBC_PROPERTIES.equals(infoType)) {
            Properties props = new Properties();

            try {
                props.load(new FileInputStream(System.getProperty("domain.name") +
                                               "config.properties"));
            } catch (Exception e) {
                e.printStackTrace();
            }
            Object dsName = props.get(Configuration.JDBC_DS_NAME);
            if (dsName != null) {
                if (((Hashtable)env).containsKey(Configuration.JDBC_DS_NAME)) {
                    ((Hashtable)env).put(Configuration.JDBC_DS_NAME,
                                         (String)dsName);
                }
            }
        }
        return null;
    }

    public void modifyInitialContext(Object object) {
    }

    public int getNumOfRetries() {
        return 0;
    }
}
-------------------------------------------------------------------------------------------------
Now this CustomEnvInfoProvider is configured as shown below

Also ensure that the property - jbo. ampool.dynamicjdbccredentials is set true (It is true by default).

Now, when the application with this AM is run, the datasource string is read from config.properties file placed in the weblogic domain home directory. Even if the datasource string is changed in properties file dynamically ( as in while AM is running), the previous value is retained for a user session. 

For further details about this concept refer the below link: