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:




Thursday, June 20, 2013

How to override activate/passivate default methods in Application module Impl class

I came across a situation where I have a instance variable in AM impl class and needed to make it passivation safe.

Actually Instances variables are not passivated by the framework. It recommends not to use instance variables by the developer. If at all someone badly needs instance variables in an AM Impl class, he needs to explicitly override 'passivateState' and 'activateState' methods(in corresponding Impl) to passivate and activate those variables.

Below code snippet is for activating/passivating a specific intance variable in an AM Impl class.

 @Override
    protected void activateState(Element parent) {
        super.activateState(parent);
        activateState(parent, LOV_FLAG);
    }
    private void passivate(Document doc,Element parent,String elemName){
        // 1. Retrieve the value of the value to save
          String value = (String)getSession().getUserData().get(elemName);
          if(value==null){
              return; //no need to passivate
          }
          // 2. Create an XML element to contain the value
          Node node = doc.createElement(elemName);
          // 3. Create an XML text node to represent the value
          Node cNode = doc.createTextNode(value);
          // 4. Append the text node as a child of the element
          node.appendChild(cNode);
          // 5. Append the element to the parent element passed in
          parent.appendChild(node);
    }
    private  void activateState(Element elem,String elemName) {
      super.activateState(elem);
      String value = null;
      if (elem != null) {
        // 1. Search the element for any  elements by elemName
        NodeList nl = elem.getElementsByTagName(elemName);
        if (nl != null) {
          // 2. If any found, loop over the nodes found
          for (int i=0, length = nl.getLength(); i < length; i++) {
            // 3. Get first child node of the  element
            Node child = nl.item(i).getFirstChild();
            if (child != null) {
              // 4. Set the  value to the activated value
              //setCounterValue(new Integer(child.getNodeValue()).intValue()+1);
              value = child.getNodeValue(); 
              break;
            }
          }
        }
      }
  
      if(value!=null){
          getSession().getUserData().put(elemName, value);
      }else{
          getSession().getUserData().remove(elemName);
      }
    }

    @Override
    protected void passivateState(Document document, Element element) {
        super.passivateState(document, element);
        passivate(document, element, LOV_FLAG);
    }

Tuesday, April 23, 2013

Using ADF Client Side Listeners & Server Side Listeners to save panelSplitter position in a managed bean

In some cases where we find the framework supported events and handlers to be inadequate and need a custom event to be handled by the components. ADF Client side and server side listeners work in conjunction with each other to suffice such a requirement.

One such example is with panelSplitter component. Let us say a user wants to save the state of panelSplitter (splitter position) in session, then below solution would be handy.

The af:clientListener and af:serverListener tags are defined for a panelSplitter component as follows:

<af:panelSplitter id="ps1" label="MySplitter">
  <af:clientListener method="propertyChangeClientListener" type="propertyChange"/>
  <af:serverListener type="mySplitterCustomEvent"
                     method="#{mybean.handleSplitterPositionChange}"/>
</af:inputText>


On changing the splitter position through user action, the client listener invokes the below javascript method - propertyChangeClientListener(). That method indirectly invokes the server side listener by raising a particular event that the server side listener is waiting for. That event here is "mySplitterCustomEvent". This event is queued in AdfCustomEvent with the parameter as splitterPosition value and a configuration value "true" to make this immediate on server.

function propertyChangeClientListener(e) {
    var inputComp = AdfPage.PAGE.findComponent(e.getSource().getClientId());
    if(e.getPropertyName().toString() == "splitterPosition"){
      var newVal = e.getNewValue();
      AdfCustomEvent.queue(inputComp, "mySplitterCustomEvent",
                                 // Send one parameter
                                 {splitterPosition:newVal},
                                 // Make it "immediate" on the server
                                 true);
    }
}

Given below is the server side listener method. This method reads the value of the splitterPosition from the custom event received and stores the value in a managed bean "resizeBean".

public void handleSplitterPositionChange(ClientEvent event){
     ResizePersistenceBean resizeBean = null;
    try{
       //Use ADFUtil class posted in my earlier blogs
        resizeBean = (ResizePersistenceBean)ADFUtil.evaluateEL("#{resizePersistenceBean}");
       if(!event.getParameters().get("splitterPosition").toString().equals("")){
        resizeBean.setSplitterPosition((int)(Float.parseFloat(event.getParameters().get("splitterPosition").toString())));
       }
    }catch(Exception e){
       resizeBean.setSplitterPosition(200);
    }
}

Thursday, April 18, 2013

Remote Debugging of ADF web applications in Weblogic

We can debug our ADF web application by having the code pointed to a stand alone weblogic server.

To achieve that the startWeblogic.sh script that is present in <domain_dir>/bin/ , should be modified to keep the below entry, usually after java -version line

JAVA_OPTIONS="-Xdebug -Djava.compiler=NONE -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,address=10171,suspend=n $JAVA_OPTIONS"

After the above change, the startWeblogic.sh is executed to bring up the server. Now the server listens at 10171 port for debugging.

Now, go to your adf application and choose the project that has the code you want to debug. Click on the Project Properties and edit the current run configuration with the host name and debug port details as shown in below pic.
Now save the project and right click on it and select "start Remote Debugger". This will make the project to attach to server at the debug port as mentioned. 

With this, when server is running the use case whose code is in the project configured, the control flows to the source code attached and can be debugged with debug points.

Thursday, April 4, 2013

Traversing ViewCriteria hierarchy

A ViewCriteria instance is an hierarchy of Criteria Rows and Criteria Items. Criteria Item is the smallest unit which contains an Operand, Operator and Value(Either Literal or Bind Variable).

A ViewCriteriaRow can have multipe ViewCriteriaItem instances and a ViewCriteria can have multiple ViewCriteriaRows. Let us assume appliedVC is an applied ViewCriteria to a ViewObject.


The below code snippet traverses the ViewCriteria and locates a particular ViewCriteriaItem and removes. Before removing it reads the value and stores it in a variable.



            boolean found = false;
            while (appliedVC.hasNext() && !found) {
                vcr = (ViewCriteriaRow)appliedVC.next();
                if (vcr != null) {
                   ViewCriteriaItem[] vcis = vcr.getCriteriaItemArray();
                   if (vcis != null && vcis.length > 0) {
                       for (int j = 0; j < vcis.length && !found; j++) {
                           ViewCriteriaItem vci = vcis[j];
                           if(vci!= null && vci.getAttributeDef()!=null && vci.getAttributeDef().getName().equals("MyFlag")){
                                System.out.println("****"+vci.getAttributeDef().getName()+"***"+vci.getValue());
                                if(vci.getValue()!=null){
                                    myFlagVal = vci.getValue().toString();
                                    vcr.removeCriteriaItem("MasterOrgFlag", vci);
                                    found=true;
                                }
                           }
                       }
                   }
                }
            }

Replacing a particular ViewCriteriaItem with custom where condition

Sometimes we may come across a situation where we want to have a field in Search Component in UI but don't want the corresponding view criteria item to be part of the where clause generated for the ViewObject. This requirement generally comes when the value set for the search field in UI is used to decide which custom where condition to be added instead of the search field itself.

This requirement makes more sense when a ViewObject is read-only based on a sql query and you want the query execution mode for the ViewCriteria to be Database.

You need to override the getCriteriaItemClause() method of the ViwObject

The below code snippet looks for the search field "ManagerOnly" which has two possible values "Y"/"N". If the Value is "Y", a custom where condition has to be returned else nothing, which means there won't be any clause generated for the corresponding criteria item.

(Assumption: There is Emp schema where Emp details are stored in Employee table. Manager is highest in hierarchy and the manager_id value for manager will be its own employee id.)

 @Override
    public String getCriteriaItemClause(ViewCriteriaItem vci) {
      if (vci.getAttributeDef().getName().equals("ManagerOnly") &&      vci.getViewCriteria().getName().contains("MyEmpViewCriteria")&& vci.getValue().equals("Y")) {
        return "EMPLOYEE_ID=MANAGER_ID";
      } else { //If the attribute is not "ManagerOnly" retain the default behavior.
        return super.getCriteriaItemClause(vci);
      }
    }