Showing posts with label ADF. Show all posts
Showing posts with label ADF. Show all posts

Monday, June 1, 2015

ADF: Creating LOVs using Programmatic View Objects

Inside ADF forms, there was requirement to have custom lookups based on Stored Procedures.

Steps we followed to create custom lookups:
  • Create “Programmatic View Objects” for all possible lookups.
  • Create one more Dummy “Programmatic View Object”, which will act as container for all other View Objects.
  • For all the fields for which lookups are needed, create a transient attribute in container View Object & configure LOV for that transient attribute.
  • In the task flow, add default activity as “CreateInsert” from container View Object data control operations. This will create a new row of Container View Object, and we will be then able to select new values for the fields.


Programmatic View Object

In certain scenarios, we need to populate data into our ADF application from custom data source (e.g. 3rd party API, DB stored procs etc).

Programmatic view objects provides us flexibility of developing such scenarios. It executes the query/logic and produces a set of rows.

For creating such programmatic view objects, we need to override some of the methods of oracle.jbo.server.ViewObjectImpl to provide basic functionality of view objects.

PFB the methods, which must be overridden:

  1. create()
  2. Sample Implementation:
    protected void create() {
       this.getViewDef().setQuery(null);
       this.getViewDef().setSelectClause(null);
       this.setQuery(null);
    } 
  3. executeQueryForCollection() – The highlighted method should be implemented & depends on the datasource of programmatic view objects.
  4. Sample Implementation:
    protected void executeQueryForCollection(Object qc, Object[] params, 
                                             int noUserParams) {
        storeNewResultSet(qc, retrieveParamsResultSet(qc, params));
        super.executeQueryForCollection(qc, params, noUserParams);
    
    }
    
  5. storeNewResultSet()
  6. Sample Implementation:
    private void storeNewResultSet(Object qc, ResultSet rs) {
        ResultSet existingRs = (ResultSet)getUserDataForCollection(qc);
    
        // If this query collection is getting reused, close out any previous rowset
        if (existingRs != null) {
            try {
                existingRs.close();
            } catch (SQLException e) {
                throw new JboException(e);
            }
        }
        setUserDataForCollection(qc, rs);
        hasNextForCollection(qc); // Prime the pump with the first row.
    }
    
  7. hasNextForCollection()
  8. Sample Implementation:
    protected boolean hasNextForCollection(Object qc) {
        ResultSet rs = (ResultSet)getUserDataForCollection(qc);
        boolean nextOne = false;
    
        if (rs != null) {
            try {
                nextOne = rs.next();
                if (!nextOne) {
                    setFetchCompleteForCollection(qc, true);
                    rs.close();
                }
            } catch (SQLException s) {
                throw new JboException(s);
            }
        }
        return nextOne;
    }
    
  9. createRowFromResultSet() – This method is custom & will vary with the datasource of the programmatic view object.
  10. Sample Implementation:
    protected ViewRowImpl createRowFromResultSet(Object qc, ResultSet resultSet) {
        resultSet = (ResultSet)getUserDataForCollection(qc);
        ViewRowImpl r = createNewRowForCollection(qc);
    
        if (resultSet != null) {
            try {
                r, r.getAttributeIndexOf("[VO attribute name]"),
                                        resultSet.getObject("[stored proc o/p param name]"));
            } catch (SQLException s) {
                throw new JboException(s);
           }
        }
        return r;
    }
    
  11. releaseUserDataForCollection()
  12. Sample Implementation:
    protected void releaseUserDataForCollection(Object qc, Object rs) {
        ResultSet userDataRS = (ResultSet)getUserDataForCollection(qc);
        if (userDataRS != null) {
            try {
                userDataRS.close();
            } catch (SQLException s) {
                s.printStackTrace();
            }
        }
        super.releaseUserDataForCollection(qc, rs);
    } 



Stored procedure based Programmatic View Object:

PFB the sample implementation of retrieveParamsResultSet(qc, params)

Note: Stored procedure is returning REF_CURSOR.
Procedure definition:
PACKAGE BODY JOBSPKG AS
    PROCEDURE JOB_PROC(filter_str in varchar2, Result_Set Out Nocopy Sys_Refcursor) IS
    BEGIN
        OPEN Result_Set FOR
        SELECT E.EMPLOYEE_ID, E.FIRST_NAME, E.LAST_NAME, J.JOB_ID, J.JOB_TITLE
        FROM EMPLOYEES E, JOBS J
        WHERE E.JOB_ID=J.JOB_ID AND E.FIRST_NAME LIKE '%' || filter_str || '%';
    END;
END;
private ResultSet retrieveParamsResultSet(Object qc, Object[] params) {
 ResultSet rs =
  StoredProcParams.getStoredProcResult(this.getjobTitle());
 return rs;
}   

public static ResultSet getStoredProcResult(Object... params) {
 ResultSet rs = null;
 CallableStatement callStmt = null;
 try {
    StringBuffer jobquery = new StringBuffer();
  jobquery.append("JOBSPKG. JOB_PROC" + "(");  //where JOBSPKG.JOB_PROC is Proc name
  for (int i = 0; i < params.length; i++){
   if(params[i] == null)
    jobquery.append(params[i] + "," );
   else
    jobquery.append( "'" + params[i] + "'," );
  }
  jobquery.append("?); end;");
                         //will depend upon the no. of output parameters, in case of 
    //more than one parameters it will be represented as ?,?,?
  callStmt = this.getDBTransaction().createCallableStatement(jobquery.toString(), 0);
  callStmt.registerOutParameter(1,
          OracleTypes.CURSOR);
  callStmt.execute();
  rs = (ResultSet)callStmt.getObject(1);
 } catch (SQLException sqlerr) {
  throw new JboException(sqlerr);
 }
 return rs;
}


Issues faced during development of Programmatic View Objects
  1. Passing input parameter values for stored procedure. For passing input value to stored procedure,
    • Create the bind variables of programmatic VO, for each of the input parameter required for Stored Procedure.
    • Write a method inside AMImpl class to set VO binding parameter & expose that in datacontrols.
    • For inputListOfValues, configure launchPopupListener & inside that method, populate the values of bind parameters of VO.
  2. <af:inputListOfValues label="#{bindings.test.hints.label}"
       popupTitle="Search and Select: #{bindings.test.hints.label}"
       id="testId" value="#{bindings.test.inputValue}"
       model="#{bindings.test.listOfValuesModel}"
       launchPopupListener="#{pageFlowScope.testBean.listen}">
     <f:validator binding="#{bindings.test.validator}"/>
    </af:inputListOfValues>
    
    Snippet from testBean.listen method
    public void listen(LaunchPopupEvent launchPopupEvent) {
            // Add event code here...
            DCBindingContainer dcBindingContainer =
               (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
            OperationBinding operationBinding =
                dcBindingContainer.getOperationBinding("setInputParameters");
            operationBinding.getParamsMap().put("filterStr ", "ohn");
            operationBinding.execute();
    }
    
    Snippet from setInputParameters method from AMImpl class
    public void setInputParameters(String filterStr){
     ViewObjRowImpl rw = (ViewObjRowImpl) getViewObj1().createRow();
     programmaticVOImpl temp = (programmaticVOImpl) rw.getprogrammaticVO1().getViewObject();
     temp.setVariable(filterStr);
    }
    
  3. Multiple calls to DB stored proc for retrieving View Object rows. Or view criteria not working for programmatic view objects.
    • If you are using view criteria for programmatic VOs, use “Query Execution Mode” as “In Memory”.

    • Override one more method in programmatic view object IMPL class.
    • protected RowIterator findByViewCriteriaForViewRowSet(ViewRowSetImpl viewRowSetImpl,
             ViewCriteria viewCriteria,
             int i, int i2,
             Variable[] variable,
             Object[] object) {
        RowIterator rwItr =
       super.findByViewCriteriaForViewRowSet(viewRowSetImpl, viewCriteria, 25,
            ViewObject.QUERY_MODE_SCAN_VIEW_ROWS, 
            variable, object);
       return rwItr;
      }
      

Wednesday, February 27, 2013

ADF: No Vertical Scrollbars for Table/Tree


Below is the way to eliminate vertical scrollbars from ADF Table/Tree component:

autoHeightRows 
This property suggests that, the height of the component can grow to a maximum of 'autoHeightRows' after which a vertical scrollbar is displayed.

Problem: But even after specifying the value in autoHeightRows or assigning it the value of model data size (e.g. list size), vertical scrollbars are visible after certain limit.

Resolution: Assign the same value to "fetchSize" property as specified for "autoHeightRows".

Tuesday, February 26, 2013

ADF: javax.faces.model.NoRowAvailableException


Scenarios:

This error is generally thrown in case where underlying tree model is changed & component changes (i.e. expanding or collapsing of nodes) are saved in state. 

  • Approach 1
It is suggested in many blogs to clear the disclosed rowkeyset & add partial trigger on tree/treeTable component
treeTable.getDisclosedRowKeys().clear();
Note: But it will work only in case where tree is already being rendered & underlining model is changed as a part of partial submission.

  • Approach 2
In case where page (having tree/treeTable component) is called again with model change. And on previous page tree nodes are expanded & collapsed randomly.
org.apache.myfaces.trinidad.model.TreeModel uses ComponentChangesMapForSession API to support back button behavior or to save the state of TreeTable/tree. We can manually clear the changes stored in session map for respective pages.
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().
  remove("org.apache.myfaces.trinidadinternal.ComponentChangesMapForSession/oracle/webcenter/portalapp/pages/testPages/TestTree.jspx");

Wednesday, January 2, 2013

ADF/RIDC: Multiple sort in RIDC


Following is the way to specify multiple sort while using "GET_SEARCH_RESULTS" service through RIDC java APIs
    ----
    ----
    DataBinder requestDataBinder = idcClient.createBinder();
    requestDataBinder.putLocal("IdcService", "GET_SEARCH_RESULTS");
    requestDataBinder.putLocal("QueryText", doc.getSearchQuery());
    requestDataBinder.putLocal("SearchEngineName", "DATABASE");

    requestDataBinder.putLocal("SortSpec",
        "ORDER BY metadataField01 asc, metadataField02 asc, metadataField03 asc");
    -----
    -----

Tuesday, January 1, 2013

ADF: Getting URL parameter values inside page region


Getting URL parameter values in page region or inside taskflow can be achieved in following ways:

  • Through groovy expression
It can be specified in region binding parameter value
${facesContext.externalContext.requestParameterMap['<parameter-name>']}

  • In Managed bean
String paramValue = 
                 (String) FacesContext.getCurrentInstance().getExternalContext()
                 .getRequestParameterMap().get("param-name");

Monday, October 8, 2012

ADF: Resolving random JBO-27122 and closed statement errors


Problem: 
  • Resolving random JBO-27122 and closed statement errors 
  • <java.sql.SQLException: Statement cancelled, probably by transaction timing out.

Resolution:

Fusion web applications are not compatible with data sources defined with the JDBC XA driver. When creating a data source on Oracle WebLogic Server, be sure to change the Fusion web application data source's JDBC driver from “Oracle's Driver (Thin XA)” to “Oracle's Driver (Thin)”. Because XA data sources close all cursors upon commit, random JBO-27122 and closed statement errors may result when running the Fusion web application with an XA data source. 

Reference: http://docs.oracle.com/cd/E24382_01/web.1112/e16182/deployment_topics.htm#ADFFD23083

Thursday, July 5, 2012

ADF: Rendering HTML code on page


Below are the ways to render HTML code on JSF page:
  • Using <af:outputText>
  • <af:outputText value="#{pageFlowScope.testbean.value}" id="ot1"
                   escape="false"/>
    
  • Using <af:richTextEditor>
  • <af:richTextEditor label="Label 1" id="rte1" readOnly="true"
                       value="#{pageFlowScope.testbean.value}"/>
where bean code is using "org.apache.commons.lang.StringEscapeUtils.unescapeHtml" API to render html code properly.
import org.apache.commons.lang.StringEscapeUtils;

public class testbean {
    public testbean() {
    }
    
    private String value = "&lt;object width="100" height="100" data="http://www.google.co.in/images/srpr/logo3w.png"&gt;&lt;/object&gt;";

    public void setValue(String value) {
        this.value = value;
    }

    public String getValue() {
        value = StringEscapeUtils.unescapeHtml(value);
        return value;
    }
}

Tuesday, May 8, 2012

ADF: Programmatic View Criteria


To define view criteria programmatically:
  • VO must contain the attributes which are to be added in where clause.
  • The following method is added in AMImpl java class & method is exposed via client interface.
import oracle.jbo.ViewCriteria;
import oracle.jbo.ViewCriteriaItem;
import oracle.jbo.ViewCriteriaRow;
import oracle.jbo.domain.Number;
...
...

public void filterEmployees(){
   ViewObjectImpl empVOImpl = getEmployeesView1();            
   ViewCriteria vc = empVOImpl.createViewCriteria();
   ViewCriteriaRow vcr = vc.createViewCriteriaRow();            
    
   //criteria for employee id
   ViewCriteriaItem vci1 = vcr.ensureCriteriaItem("JobId");
   vci1.setValue("SH_CLERK");

   //criteria for showing employees whose salary are more than 10000
   ViewCriteriaItem vci2 = vcr.ensureCriteriaItem("Salary");
   vci2.setOperator(">");
   vci2.setValue(new Number(2500));

   //criteria for department
   int[] deptIds = {50,100};
   ViewCriteriaItem vci3 = vcr.ensureCriteriaItem("DepartmentId");
   vci3.setOperator("IN");
   int i = 0;
   for(int deptId: deptIds){
     vci3.setValue(i++, new Number(deptId));
   }

   vc.addElement(vcr);
   empVOImpl.applyViewCriteria(vc);
   System.out.println("Query: " + empVOImpl.getQuery());
   empVOImpl.executeQuery();
}

Above implementation is showing three conditions:
  • Equal (JobId = "SH_CLERK")
  • Greater than (Salary > 2500)
  • IN (DepartmentId in (50,100))

ADF: Programmatically adding partial trigger/target


To refresh component programmatically:
  • Bind the component in bean. e.g.
<af:outputtext binding="#{pageFlowScope.BackingBean.outputText}" id="ot1" 
               value="outputText1">
</af:outputtext>

  • Wherever required use following API inside bean:
AdfFacesContext.getCurrentInstance().addPartialTarget(getOutputText());

Monday, May 7, 2012

ADF: Access attribute binding

import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;

import oracle.binding.AttributeBinding;
...
...
    DCBindingContainer bindings = 
        ((DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry());
    AttributeBinding attrBinding = 
        (AttributeBinding) bindings.getControlBinding("AttributeNameFromBindings");

    //get input value
    attrBinding.getInputValue();
    //get label
    attrBinding.getLabel();
Similiarly, we can cast bindings.getControlBinding("AttributeNameFromBindings") to:
  • JUCtrlListBinding: for getting value from list binding.

ADF: Access/Execute method binding


Below is the way to access/execute method binding from managed bean:
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;

import oracle.binding.OperationBinding;
...
...
    DCBindingContainer bindings = 
        ((DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry());
    OperationBinding operationBinding = 
        bindings.getOperationBinding("MethodNameFromBindings");
    
    //passing value into method arguments
    operationBinding.getParamsMap().put("ArgumentName",value);
    operationBinding.execute();
    
    //getting return value from method
    operationBinding.getResult();

Friday, May 4, 2012

ADF: Programmatic Redirection


Below is the way to programmatic navigate to other view:
    public void redirectToSelf(String viewId) {
        FacesContext fctx = FacesContext.getCurrentInstance();
        ExternalContext ectx = fctx.getExternalContext();
        ControllerContext controllerCtx = null;
        controllerCtx = ControllerContext.getInstance();
        String activityURL = controllerCtx.getGlobalViewActivityURL(viewId);
        try {
            ectx.redirect(activityURL);
        } catch (IOException e) {
            //Can't redirect
            e.printStackTrace();
        }
    }
Note: For executing "controllerCtx.getGlobalViewActivityURL", the view must be present in adfc-config.xml .

ADF: Programmatic Logging out


Below are the ways to programmatically logging out the user:
  • First Approach
FacesContext fc = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession)fc.getExternalContext().getSession(false);
session.invalidate();

try {
   fc.getExternalContext().redirect("faces/welcome");
} catch (IOException e) {
   e.printStackTrace();
}
  • Second Approach
ExternalContext ectx = FacesContext.getCurrentInstance().getExternalContext();
StringBuilder logoutURL = new StringBuilder(ectx.getRequestContextPath());

logoutURL.append("/adfAuthentication?logout=true&end_url=");
logoutURL.append("/faces/welcome");

try {
   ectx.redirect(logoutURL.toString());
} catch (IOException e) {
   adfLogger.warning("Error logging off");
   e.printStackTrace();
}

ADF: Programmatic Navigation


Below is the API that can be used to programmatically navigate to next/new activity of taskflow:
FacesContext facesContext = FacesContext.getCurrentInstance();
NavigationHandler navHandler = facesContext.getApplication().getNavigationHandler();
navHandler.handleNavigation(facesContext, null, "controlFlowCaseName");
API javadoc:
Package: javax.faces.application.NavigationHandler

public abstract void handleNavigation(FacesContext context, String fromAction, String outcome)

Perform navigation processing based on the state information in the specified FacesContext,
plus the outcome string returned by an executed application action.

Parameters:
context - The FacesContext for the current request
fromAction - The action binding expression that was evaluated to retrieve the specified outcome,
or null if the outcome was acquired by some other means
outcome - The logical outcome returned by a previous invoked application action (which may be null)
Throws: NullPointerException - if context is null

Thursday, May 3, 2012

ADF: Tree using managed bean


For creating ADF tree component using managed bean:
  • Create a object representing tree item & this will have references to its children.
public class TreeNode {
    public TreeNode() {
        super();
    }    
    public TreeNode(int id, String name){
        this.id = id;
        this.name = name;
    }
    
    private int id;
    private String name;
    private List<TreeNode> childNodes;

    ...with getters & setters...
}
  • Create and Populate objects representing tree item with data, i did that in constructor of managed bean. Managed bean can be registered in either adfc-config.xml or faces-config.xml.
    public TreeBackingBean() {
        rootNode = new TreeNode(1, "World");
        TreeNode node2 = new TreeNode(2, "USA");
        TreeNode node3 = new TreeNode(3, "India");
        TreeNode node4 = new TreeNode(4, "Los Angeles");
        TreeNode node5 = new TreeNode(5, "Delhi");
        TreeNode node6 = new TreeNode(6, "Mumbai");
        
        List<TreeNode> l1 = new ArrayList<TreeNode>();
        l1.add(node2);
        l1.add(node3);
        rootNode.setChildNodes(l1);
        
        List<TreeNode> l2 = new ArrayList<TreeNode>();
        l2.add(node4);
        node2.setChildNodes(l2);
        
        List<TreeNode> l3 = new ArrayList<TreeNode>();
        l3.add(node5);
        l3.add(node6);
        node3.setChildNodes(l3);
    }
  • Use following API to create TreeModel:
org.apache.myfaces.trinidad.model.TreeModel treeModel = 
new org.apache.myfaces.trinidad.model.ChildPropertyTreeModel(objectOfNodeTreeItem, "childNodes");
Note: The second argument is String matching the property of objectOfTreeItem representing references to its children. If the name is mismatched, you will end up with "javax.el.PropertyNotFoundException" error.

  • Bind the value of <af:tree> from page to above "treeModel" instance. Final result be like:
The sample application can be downloaded from https://rapidshare.com/files/4090558697/TreeFromBeanExample.zip

Tuesday, May 1, 2012

ADF: Ghost Labels

To have Ghost Labels on ADF pages:
    <af:document id="d1">
      <af:messages id="m1"/>
      <f:facet name="metaContainer">
        <af:resource type="javascript" source="/jquery-1.7.min.js"/>
        <af:resource type="javascript" source="/label-in-field.js"/>
        <af:resource type="css">
            .text-label {
                color: #cdcdcd;
                font-weight: bold; 
            }
        </af:resource>
      </f:facet>
      <af:form id="f1">
        <af:panelStretchLayout id="psl1">
          <f:facet name="center">
            <af:panelGroupLayout layout="scroll" id="pgl1" halign="center">
              <af:inputText label="Username" id="it1" simple="true"
                            required="true"/>
              <af:inputText label="Password" id="it2" secret="true"
                            simple="true" autoTab="true"/>
              <af:selectOneChoice value="#{bindings.DepartmentId.inputValue}"
                                  id="soc1">
                <f:selectItems value="#{bindings.DepartmentId.items}" id="si1"/>
              </af:selectOneChoice>
            </af:panelGroupLayout>
            <!-- id="af_one_column_stretched"   -->
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
      <af:clientListener type="load" method="onPageLoad"/>
    </af:document>
Final Result will be like:
Note: http://lspatil25.blogspot.in/2012/05/adf-make-findcomponentbyabsoluteid-js.html

ADF: Make findComponentByAbsoluteId JS work


JS: AdfPage.PAGE.findComponentByAbsoluteId(componentId); 
To make it work:
  • There must be some validation/behaviour property or client attribute present in that component (as in figure, maximum length is present).
  • Or the component itself should be client component.
<af:inputText label="Label 1" id="it1">
   <af:clientAttribute name="defaultFocusField" value="Y"/>
</af:inputText>

Monday, April 30, 2012

ADF: Default cursor/focus to input field using javascript


Sometimes when default properties won't work, we can use JS to set initial focus.
  • jquery is required & can be downloaded from (http://jquery.com/).
  • A dummy <af:clientAttribute> is added for field on which initial focus is required.
  • A <af:clientListener> method is added on "load" type.
<f:view>
    <af:document id="d1">
      <f:facet name="metaContainer">
        <af:resource type="javascript" source="/jquery-1.7.min.js"/>
        <af:resource type="javascript">
          function onLoadFocus() {
              $('input[type="text"]').each(function () {
                  if (isDefaultFocusField(this.name) == 'Y') {
                      this.focus();
                  }
              });
          }

          function isDefaultFocusField(compId) {
              try {
                  var adfComp = AdfPage.PAGE.findComponentByAbsoluteId(compId);
                  return adfComp.getProperty("defaultFocusField");
              }
              catch (err) {
              }
          }
        </af:resource>
      </f:facet>
      <af:form id="f1">
        <af:panelStretchLayout id="psl1">
          <f:facet name="center">
            <af:panelGroupLayout layout="scroll" id="pgl1">
              <af:inputText label="Label 1" id="it1">
                <af:clientAttribute name="defaultFocusField" value="Y"/>
              </af:inputText>
              <af:inputText label="Label 2" id="it2"/>
            </af:panelGroupLayout>
            <!-- id="af_one_column_stretched"   -->
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
      <af:clientListener type="load" method="onLoadFocus"/>
    </af:document>
  </f:view>
Note: http://lspatil25.blogspot.in/2012/05/adf-make-findcomponentbyabsoluteid-js.html

ADF: Default cursor/focus to input field & default button of JSF page


For setting initial cursor/focus to input field:
  • The property named "initialFocusId" should be set of <af:document>.
  • The property named " clientComponent" should be set to "true" for that input field.

For setting default button for the form:
  • The property named "defaultCommand" of "<af:form>" should be set. By setting this property, we can press "Enter" & the button's action will be invoked.
    <af:document id="d1" initialFocusId="it1">
      <af:form id="f1" defaultCommand="cb1">
        <af:panelStretchLayout id="psl1">
          <f:facet name="center">
            <af:panelGroupLayout layout="scroll" id="pgl1">
              <af:inputText label="Label 1" id="it1" clientComponent="true"/>
              <af:inputText label="Label 2" id="it2"/>
              <af:commandButton text="commandButton 1" id="cb1"/>
            </af:panelGroupLayout>
            <!-- id="af_one_column_stretched"   -->
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
    </af:document>
Note: For jsff pages, we can use <af:subform>.
<af:subform defaultcommand="cb1" id="s1" />