ads

X

Tuesday, April 29, 2014

Back Bean Switching For Dynamic Region | Oracle ADF

How to control your ADF Application using One Dynamic Region & Use it to switch among multiple task Flows in smart way using Back Beans without using Switcher UI Component using default Task Flow Bean created while creating Task Flow as Dynamic Region.

Solution point, is how to modify .xml url using dynamic Task Flows Names.

private String taskFlowId = "/WEB-INF/DefaultTask.xml#DefaultTask";
setDynamicTaskFlowId("/WEB-INF/" + getMySwitchingVal() + ".xml#" + getMySwitchingVal());

- before implementing this solution, you have to define multiple Task Flows with cleared names and binding it to Back Bean.



- I Have Menu items to control My Dynamic Switcher to Execute Back Bean Method passing Task Flow Name before using set property listener.


 - Now, we can build our Back Bean Method as below to control it (getMySwitchingVal is getting the task flow name based on set property listener from your Action UI Component).


Notes

1- For Business Needs, we made Auto Roll Back before Switching.
2- PrepareBeforeBavigate Method is just s simple Method to cover business before switching.

References




Thursday, April 17, 2014

Update Model Values while Validation (Exception) occurs on page on Value Change event- Oracle ADF (processUpdates)

In ADF when we have some validation on page and we try to submit any value, but normally after validation (Exception) values are not updated in Model until exception is handled.
This tutorial is about how to update values in Model while exception on page.
For a scenario, i have used department (default HR Schema) table and form in a JSP XML fragment inside bounded taskflow.
In form Department Id and Department Name is mandatory field, ManagerId and Location Id has auto submit true.

  • I have dropped createInsert,Execute,Commit and Rollback as buttons on page
  • Click on create button and first enter manager id, as this field has autosubmit true so on submission of value page is validated and then Required Fields throw exception, and after exception you can see that manager id & location id is not updated in Model, and not reflected in af:table also.


  •  Normally what we do, set immediate true to skip validations, but in this case i have witten ValueChangeListener on manager Id and Location Id to process values in Model.- see code

  •     public void mangrIdVCE(ValueChangeEvent vce) {
            vce.getComponent().processUpdates(FacesContext.getCurrentInstance());
        }
    

  • processUpdates() method of UIComponent class perform the coponent tree processing for all facet of this component by Update Model Values phase of request processing, and pushes data to Model

  • Now after calling this method, run application- Now values are populated in af:table and validation is still there on page


  • this approach can be used while using validators on page and want to process some values after validation (as conditional validation of some fields etc)

Get updated model values in value change listener instantly, ProcessUpdates in ADF (a beauty)

Get updated model values in value change listener instantly, ProcessUpdates in ADF (a beauty)

hello all,
this posts talks about updating model instantly when a attribute is changed on page level.
In ValueChangeListener of component if we try to get new value from Model layer, it always returns old value, then we have only one option to get new value, use event object (evt.getNewValue();)
but sometimes we need to update model same time
So how to do this-

  • Created a Fusion Web Application using Departments table of HR Schema (Oracle)


  • dropped Departments Vo on page as a form, and created ValueChangeListener on DepartmnetName field to get new value 


  • now i have written a code to get DepartmentName from current row of Departments VO (ViewObject) and to get from ValueChangeEvent

  • Method in AMImpl- Called in ValueChangeListener through binding Layer

    /**Method to print Department Name */
        public void getDeptNameAction() {
            ViewObject depart = this.getDepartments1();
            Row curRow = depart.getCurrentRow();
            if (curRow != null) {
                System.out.println("Current Department from model is-" + curRow.getAttribute("DepartmentName"));
            }
        }
    


    Code in ValueChangeListener - AM Method Called and Code to get new value from event itself

        /**Value change listener for Department Name
         * @param vce
         */
        public void deptNmVCE(ValueChangeEvent vce) {
            if (vce.getNewValue() != null) {
                System.out.println("New Value in VCE-" + vce.getNewValue());
                BindingContainer bc = BindingContext.getCurrent().getCurrentBindingsEntry();
                OperationBinding ob = bc.getOperationBinding("getDeptNameAction");
                ob.execute();
            }
        }
    

  • In this case when i have changed value on page 



  • see the result -output from valuechangelistener- Model layer returns old value :-(


  • Now to update value in Model layer , i have just called processUpdates in ValueChangeListener- See the code

  •     /**Value change listener for Department Name
         * @param vce
         */
        public void deptNmVCE(ValueChangeEvent vce) {
            if (vce.getNewValue() != null) {
                //Method to update Model in VCE
                vce.getComponent().processUpdates(FacesContext.getCurrentInstance());
                System.out.println("New Value in VCE-" + vce.getNewValue());
                BindingContainer bc = BindingContext.getCurrent().getCurrentBindingsEntry();
                OperationBinding ob = bc.getOperationBinding("getDeptNameAction");
                ob.execute();
            }
        }
    

  • see the result -Value is updated in model instantly (in VCE)
so this is how processUpdates works

ViewObject Validation rules for transient attributes (Model level validation in ADF)

this post is about model level validation for transient attributes, often we need to apply some common types of validation on transient attributes (as for negative value, for email, for phone number)
in ADF ViewObject there is a editor for validation rules for transient attributes, so now see how to use that editor and rules



  • create a fusion web application and prepare model layer using Departments table of HR schema


  • then create a transient attribute in viewObject and you can see that Validation Rules tab appears only in case of transient attribute


  • now click on add icon to add new validation rule for attribute, you can see there are multiple types for that validation rule 




  • in this post i have used Compare and Regular Expression rules, suppose i have to check that value of transient attribute must not be 100 , so for that rule will be like this, and write a proper message for failure handling 



  • now run your BC4J tester and check , a window with error message appears there in case of failure

  • you can also check this validation on your page, some more- i have added one more transient field for Email Address and applied a regular expression to validate this field (ADF by default provides regular expression for Email and Phone Number pattern for others you can use your own pattern)


  • after model level configurations , i have dropped Departments ViewObject on page as ADF form, here you can see both validation are working 




  • Thanks & Cheers :-) happy learning

Thursday, April 10, 2014

Bindings

Binding provides objects to link components, it is the most innovative part of ADF after Task Flows and provides direct interaction with Appication Module. We can work with bindings in ManagedBean and also in Expression Builder. Bindings are basically used for the interaction on the model and the viewController in an ADF Application.

When we drag any DataControl on page then a binding entry is made in the pageDef of the page for that particular field. Suppose we want to Use CreateInsert operation on a button clik in the page, then we have to create the binding of CreateInsert action in the pageDef on that page and then we can use this action in our ManagedBean to perform the desired operation.

Oracle ADF provides following types of bindings:


  1. Action Binding

    Action Binding is specifically defined for a button component. Provides access to operations defined by the business object. Such as CreateInsert , Delete.
    //The code below gets the Bindings entry of the binding container and fetches the current bindings entry
     BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
     //Gets the opertaion bindings for the method "CreateInsert"
     OperationBinding operationBinding = bindings.getOperationBinding("CreateInsert");
     //Then execute executes the method of the binding
     operationBinding.execute(); 
     
    Likewise you can use the above code to call bindings in ManagedBean.
  2. Iterator Binding
  3. When we drag tables on the page in ADF then ADF creates a IteratorBinding in the pageDef of the page. The iterator binding can be used to get the iterator of the table in the ManagedBean.
  4. Given below is the code that can be used to get the IteratorBinding in the ManagedBean.
        BindingContext btx = BindingContext.getCurrent();
        DCBindingContainer dcbct = (DCBindingContainer)btx.getCurrentBindingsEntry();
        DCIteratorBinding binding = dcbct.findIteratorBinding("StudentIterator");
        Row currentRow = binding.getCurrentRow();
  5. Here in the last line I have get the current Row from th iterator. 
    LikeWise you can use it to perform desired action. 
  6. 
    
  7. ValueBindings
    Value Bindings are created in the pageDef for all the attributes that are added on the page from the dataControl.

Optimising the Validator Logic, Using af:attribute to make validation easy

This post I will try to focus on how we can make validations in an editable table more effectively.

So what would we do if we have two attributes in a row in a table and we need to validate one on the basis of the other?

The answer is simple.


  • We will get the instance of the viewObject 
  • Then get the current row 
  • Then we will get the attribute with which we need to compare and then we will compare and check the validation.
This process is fine, but i have a more easy and effective way to do so.
I have created a Transient attribute in EmployeeVO where the newSalary of the Employee is to be entered. We have to make sure that the new Salary is greater than old salary.
Drag the EmployeeVO into the page. Then add an af:attribute component in the inputText of newSalaryTrans . Give attribute name and a value here i have used 'oldSal' and assigned with value of old Salary i.e. #{row.Salary}.
Now we need to implement the validation and for that I created a Validator on the NewSalaryTrans field.

In the bean I have used the code given below :

    public void newSalaryVAL(FacesContext facesContext, UIComponent uIComponent, Object object) {
        Object val = uIComponent.getAttributes().get("oldSal");
        System.out.println("New Sal is  : "+object+" Old Sal is  : "+val);
        if(val != null){
            BigDecimal oldVal = (BigDecimal)val;
            BigDecimal newVal = (BigDecimal)object;
             if(oldVal.compareTo(newVal) >=0 ){
                throw new ValidatorException(new FacesMessage("New Salary Cannnot be less than Old Salary!"));
            }
        }
    }
On putting less value in newSalaryTrans

Pagination in ADF table with Jdeveloper 12.1.2 (ADF 12C) - returned back

pagination in ADF table was very requested feature after Jdev & ADF 10g.
now in 12C again it is here and quite simple


  •  Created a fusion web application using Departments table of HR Schema and dropped it on page as ADF table


  • now suppose i want to show pagination after 5 rows so to achieve this follow these steps
  • Select table in structure window and go to property inspector change it's scrollPolicy to page ,scrollPolicy is mechanism to scroll data inside table


  • Now select table on page and go to binding tab and select IteratorBinding of Departments table and change rangeSize to 5, by default it is 25 (means fetched 25 rows at a time)


  •  set autoheightRows to 0 and styleClass to AFStretchWidth and you are done now run your page


  • It works good in mozilla ,chrome and IE, but sometimes you can face a problem of finding current row as it may be in second page :-(

Programmatic ViewObjects in ADF, Populating programmatic viewObject with an ArrayList

Today I am going to demonstrate about how to make programmatic viewObject in ADF.
There are basically 4 types of viewObjects.

1. ViewObjects based on Entity.
2. ViewObjects based on SqlQuery (also called Readonly viewObject)
3. Static viewObjects (contains fix now of rows)
4. Programmatic viewObjects (Programmatic viewObjects are viewObjects that are not populated from an sql query).

For understanding  the working of programmatic viewObjects, you must first understand the lifecycle of a viewObject. You need to know what methods are called when a viewObject is executed. Here is the sequence of methods that are called when a viewObject is executed.

LIFECYCLE

When a viewObject is called the following methods are executed in the given sequence. 
  • At first when the viewObject is first executed the method first called in the ViewObjectImpl is
    executeQueryForCollection(Object qc, Object[] params, int noUserParams)
    This method executes the Database Query in the viewObject and then calls the next method.

  • After executeQueryForCollection is executed then method hasNextForCollection(Object qc) is called. This method checks if the collection returned have a row or not. If hasNextForCollection(Object qc) returns True then the next method of the lifeCycle is called which converts the row to ADF understandable form i.e. into ViewObjectRowImpl from.

  • So when method hasNextForCollection retuns true then method createRowFromResultSet(Object qc, ResultSet resultSet) is called and this method converts the row into ADF understandable form.

  • This goes on until all the rows are covered and there is no rows left in collection. When there are no rows in the collection then the method hasNextForCollection returns false .

  • Then method setFetchCompleteForCollection(java.lang.Object qc,boolean val) is called and it sets the flag for fetch completion. This indicates that the rows from the collection are fetched.
- See more at: http://adfjavacodes.blogspot.in/2013/12/how-viewobjects-get-executed.html#sthash.whg7AwM9.dpuf

Now create an ADF Application and create a programmatic viewobject



Then press next and define attributes for the viewObject




Generate the following java classes




Click Finish.


Now we have made the viewObject. We will have to override the lifeCycle methods. Here is the code that I have used for this viewObject.


package programmaticvotestapp.model.views;

import java.sql.ResultSet;

import java.util.ArrayList;

import oracle.jbo.Row;
import oracle.jbo.server.ViewObjectImpl;
import oracle.jbo.server.ViewRowImpl;
import oracle.jbo.server.ViewRowSetImpl;

import programmaticvotestapp.model.EmpDC;
// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Wed Apr 09 14:28:40 IST 2014
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------
public class ProgrammaticVOImpl extends ViewObjectImpl {
    private ArrayList<EmpDC> empList = new ArrayList<EmpDC>(); 
    /**
     * This is the default constructor (do not remove).
     */
    public ProgrammaticVOImpl() {
    }

    /**
     * executeQueryForCollection - overridden for custom java data source support.
     * This method is executed at first.
     * So in this method we need to load our data source . 
     * In simple words initialize the list
     */
    protected void executeQueryForCollection(Object qc, Object[] params, int noUserParams) {
        // Populate the list for the firstTime
        populateArrayList();
        //To set the initial position for fetch to start
        setFetchPosition(qc,0);
        super.executeQueryForCollection(qc, params, noUserParams);
    }

    /**
     * hasNextForCollection - overridden for custom java data source support.
     * This method is called after executeQueryForCollection to check if any row exist in the datasource or not.
     * When returned true, createRowFromResultSet is called and a new row from the rowset is created.
     */
    protected boolean hasNextForCollection(Object qc) {
        return getFetchPosition(qc) < empList.size();
    }

    /**
     * createRowFromResultSet - overridden for custom java data source support.
     * creates a newRow and adds it to viewObject
     */
    protected ViewRowImpl createRowFromResultSet(Object qc, ResultSet resultSet) {
        int fetchPosition = getFetchPosition(qc);
        System.out.println("Fetch Position is : "+fetchPosition);
        ViewRowImpl newRow = (ViewRowImpl)createNewRowForCollection(qc);
        EmpDC c = empList.get(fetchPosition);
        // Setting value in the new row which is created.
        newRow.setAttribute("EmpId", c.getEmpId());
        newRow.setAttribute("EmpName", c.getEmpName());
        newRow.setAttribute("Salary", c.getSalary());
        // Updating the fetch Position
        setFetchPosition(qc, fetchPosition+1);
        
        return newRow;
    }

    /**
     * getQueryHitCount - overridden for custom java data source support.
     */
    public long getQueryHitCount(ViewRowSetImpl viewRowSet) {
        long value = empList.size();
        return value;
    }
    /**
         * Method to set the new fetch position
         * @param rowset
         * @param position
         * To set the position on the nextRecord to fetch i.e. next record of arrayList
         */
        private void setFetchPosition(Object rowset, int position) {
            if (position == empList.size()-1) {
                setFetchCompleteForCollection(rowset, true);
            }
            setUserDataForCollection(rowset, new Integer(position));
        
    }
    /**
         * Method to get the current fetch position
         * @param rowset
         * @return
         * 
         * This method gets the fetchPosition to fetch the row from the arrayList to retrieve the data
         */
        private int getFetchPosition(Object rowset) {
            int value = ((Integer)getUserDataForCollection(rowset)).intValue();
            return value;
        }
    
    public void populateArrayList(){
        empList.clear();
        empList.add(new EmpDC(1,1100,new StringBuffer("First Employee")));
        empList.add(new EmpDC(2,2100,new StringBuffer("Second Employee")));
        empList.add(new EmpDC(3,1300,new StringBuffer("Third Employee")));
        empList.add(new EmpDC(4,1700,new StringBuffer("Fourth Employee")));
        empList.add(new EmpDC(5,1200,new StringBuffer("Fifth Employee")));
        empList.add(new EmpDC(6,5100,new StringBuffer("Sixth Employee")));
        empList.add(new EmpDC(7,1900,new StringBuffer("Seventh Employee")));
        empList.add(new EmpDC(8,1200,new StringBuffer("Eight Employee")));
        empList.add(new EmpDC(9,1200,new StringBuffer("Ninth Employee")));
        empList.add(new EmpDC(10,1100,new StringBuffer("Tenth Employee")));
    }
}

on running the application you can see



You can download the sample application here ProgrammaticVoApp

Likewise you can use other datasources to populate the programmatic viewObject. The above model shows a very simple implementation, use cases can be much complex.

Making three level dependent LOV (List Of Values)

During the development we often come across a scenario in which we need to filter the List Of Values dependent on other List Of Values. Suppose you want only the cities of the selected country.

Here am about to show how we can make a three level dependent ListOfValues.
I will make a LOV which the user will select a location from locationLOV , then the other departmentLOV will show the department of the selected location, then the user will select a department and the employeeLOV will show the Employees of the selected department.


So, I have created an ADF application and made a connection to HR Schema. Also created three viewObject LovDepartmentVO,LovLocationsVO and LovEmployeeVO to make LOV's.

Now our main purpose is to filter the department on the basis of locations and employees on the basis of department.

For that we have to create a viewCritria in DepartmentsVo to filter departments on the basis of locations.


And then create view criteria for EmployeesVO.


Now we have viewObjects for LOV ready, we need to make a viewObject on whose attributes we will put these lovs.

So here I am creating a query from dual.
And then add three transient attributes as location , department and employees.
Make sure that property "updatable" is true.


Select locationsTrans and go to List of Values and add the lov by selecting LovLocationsVO.
Select departmentTrans and make a lov from lovDepartmentsVO.
Now go to view Accessors tab of the TemporaryVO you will see the lovs added to temporaryVO, now select the lovDepartment and click on edit button.

Select the viewCritera and in the bind value parameters select locationsTrans as value. This will filter the departments Lov on the basis of selecetd location.

Now add the lov on employeesTrans too and then go to viewAccessors select and mapp the bindVarible value to departmentTrans.



Now create am Applicaiton Module and add temporaryVo to AM and create a page and drag all the three attributes in the page and run the application.

Run the page and Select LocationTrans as London, then you will see DepartmentTrans filtered and you will see only 'Humar Resource' department in the lov as it resides in London. And in EmployeeTrans you will see Employees of Selected  department.


Make sure that the all the attribute have their autosubmit attribute to 'true' and attributes are partial trigged on basis of the source.

And the most important thing is that this technique will work on when the three attributes are on the same viewObject.. like in this case is the TemporaryVO.

You can Download the sample from here DependentLovTest.jar

Tuesday, April 8, 2014

Application Module Connection Sharing - Code Example Proof

There is a excellent and very informative video on AM Connection Sharing by Chris Muir where he describes how Multiple Root application modules due to sharing of ADF Library jars can be made to use single connection under the covers by using powerful feature of transaction management in task flows. Thanks to Chris for explaining this wonderful concept. Here is the link to the video AM Connection Sharing .

In this post we will see this powerful concept with the help of code example. Below is the concept:

"If there are multiple root AM's may be due to shared libraries, if task flows are used with default No Controller Transaction option, then there will be as many number of connections to DB as number of Root AM's. On the other hand if task flows in the application uses any of the three Transaction Option, then there will be Only Single connection to the DB be there any number of Root AM's , i.e. connection will be shared."



Below is the picture of the workspaces and components we will be using in jdeveloper.


The first workspace is CommonWorkspace. This has simple EO , VO and Application Module. This is shared as ADF Lib jar to the second project. Second project is named as CustomFirstWorkspace (There can be CustomNworkspaces) . This project has its own EO, VO and AM named CustomEO,CustomVO and CustomFirstApplicationModule.

Now Since CommonWorkspace is imported as ADF Library Jar in CustomFirstWorkspace, it has two DataControls because of two ApplicationModules. This in turn means it has TWO DB connections.

The View Project has a Task Flow, which has a jsff which uses VO from both the AM(DC) to create a table and a form using each VO respectively.

Lets get started and do actual coding :)


Create a Fusion Web App in Jdev as below. Its a simple app having EO,VO and Application module in model project. View Project is empty. Used HR schema with HR as user to connect to DB.



Now create a deploy profile of Model project as Create ADF Lib Jar . The jar will be created.

Now create a new application CustomFirstApplication with default EO,VO and AM but with different names.



Create a Resource connection and add ADF Lib jar of CommonWorkspace to ViewController project of CustomFirstApplication.



Now since the CustomFirstApplication project is having adf lib jar of CommonWorkspace project, there will be two DataControls for both AM's as shown.

Now create a Task Flow in view project of CustomFirstApplication. Create a JSFF inside it and use EmployeesVO1 from first DC to create a table and EmployeesCustomVO1 from second DC to create a form.



The page.jsff has tow VO's which correspond to two different AM (means 2 different DB Connections on run time)
Lets run the page and verify. Create a test.jspx and add taskflow as region and run it.
Below is the output of the page.

Now if we check the DB for user sessions, it shows 2 DB session as below.

This is the case when we used default (No Controller Transaction) option with the Task Flow.

Now lets stop the server and change the Task Flow Transaction option. Use one of the three transaction options. Here Use Existing If Possible option is used.




Now save and run the page again. When page runs, check the number of session in the DB.



We can see , by using just Transaction option in Task Flows, the connection is shared under the covers, even though there are different Root AM's due to shared libraries.

If two user sessions are there then application with task flow with No Controller Option will have 4 DB sessions while application with TaskFlow Option set will have only 2 sessions.  No Controller Option can really impact the performance if there are more shared libraries and large users that access the application.