JDev: Custom selectionListener for ViewObjects in ‘RangePaging’ mode

Lately a question on the Oracle JDev forum came up, asking for a solution for a problem with a ViewObject in ‘RangePaging’ mode and a single selection af:table defined on this ViewObject. The problem is to get the current selected row in such a case. Under some circumstances (which are not always reproducible) using the default

#{bindings.YOUR_VIEWNAME.collectionModel.makeCurrent} 

doesn’t mark the selected record and a call in a bean to get the selected record returns null:

BindingContext lBindingContext = BindingContext.getCurrent();
BindingContainer lBindingContainer = lBindingContext.getCurrentBindingsEntry();
DCBindingContainer bindingsIte = (DCBindingContainer) lBindingContainer ;
DCIteratorBinding dciter = bindingsIte.findIteratorBinding("YOUR_VIEWNAMEIterator");
Row row = dciter.getCurrentRow();
if (row == null) {
    return null;    // no current row
}

For ViewObjects in ‘Scrollable’ mode you get the selected record without any problem. ViewObjectes in ‘RangePaging’ mode are mostly used for tables which contain many rows and the use case doesn’t allow to filter the result set to a reasonably number. The ‘RangePaging’ option is a tuning parameter in the ViewObject definition

Set a ViewObject to 'RangePaging' mode

Set a ViewObject to 'RangePaging' mode


I run into this condition myself and use the following work around:

  1. remove the current selectionlistener from the table (#{bindings.YOUR_VIEWNAME.collectionModel.makeCurrent})
  2. define a new selection listener (use the small arrow on the right side) in a bean of your choice. The scope of the bean has to be view or pageflow depending on where you need access to the selected row
  3. in the new selectionListener you get the selected row from the event, get the key of the row and store it in a bean attribute
  4. when you need the selected row you use the stored row key and work with this. If you need attributes from the row you have to query the row again, as you only have the key

If you only need the key of the row you can e.g. pass this key to a service method defined in the application module or the ViewObject. Here is a sample of such a selection listener:

public void singleSelectionListener(SelectionEvent selectionEvent) {
        RowKeySet rksAdd = selectionEvent.getAddedSet();
        if (rksAdd.isEmpty())
            return;  // no selection
 
        Object[] it = rksAdd.toArray();
        // as this is for single selection there should only be one, but...
        for (Object obj: (List) it[0]) {
            mLogger.fine("Selected :" + obj);  // log selected row
            Key k = (Key) obj;   // the object is the row key
            Object[] kv = k.getKeyValues();  // get the key value for later
            // strore the key value in a bean attribute: mLastSelectedOID is defined in the bean
            mLastSelectedOID = (Integer) kv[0]; // store the key value (if the key has multiple parts you need to store them all)
        }
    }

The variable mLastSelectedOID is defined in the bean. The type of the attribute depends on the type the primary key of the table has. If you like you can generate getter/setter methods for the attribute and use them instead of assigning the value directly.

Advertisements

Save (most of) your changes to the JDeveloper IDE

Under some circumstances you may need to rename or remove your system11.x.x.x folder to overcome errors with the IDE which can’t be solved otherwise. I blogged about this How to find and reset the system11.x.x.x folder for a JDeveloper installation.
However, this means that you loose all of the changes you made to IDE, e.g. connections to DBs, code templates and many more.
In the current version or JDeveloper there is no feature to save all the changes you made with a simple export or click on a button. An ER for this is about to be filed.

Until we see this ER implemented you need to store the changes you made yourself. First of all we need to know all the places were changes can be saved in a file or exported into a file. I put all those files under source control. This makes them available for other developers in the same team and allows to hold different configurations for different or the same JDeveloper version. This blog post lists all the places I know. If you know any other place, please drop a comment so that I can include it here.
To my knowledge the locations below are working for all JDeveloper version from 11.1.1.x up to the current version 11.1.2.1.0.

There are three locations where you can save your changes

  1. Recource Palette
  2. Preferences
  3. File menu

Lets start with the Resource Palette:

Resource Palette 1

Resource Palette 1


This will bring up
Resource Palette 2

Resource Palette 2


This dialog allows you to save all the connection to DBs and Weblogic Servers you have defined as well as Catalogs. All you have to do is to specify a path and a file name to store the information.

Next are the Preferences. Here are a couple of places where you can export your changes.
Audit Profiles:

Audit Profiles

Audit Profiles

Code Editor – Code Style

Code Editor - Code Style

Code Editor - Code Style

Code Editor – Code Templates

Code Editor - Code Templates

Code Editor - Code Templates

Code Editor – Syntax Colors

Code Editor - Syntax Colors

Code Editor - Syntax Colors

Database – SQL Formatter

Database - SQL Formatter

Database - SQL Formatter

Database – SQL Formatter – Oracle Formatting

SQL Formatter - Oracle Formatting

SQL Formatter - Oracle Formatting

Database – SQL Formatter – Other Vendors (each vendor can be saved)

 Database - SQL Formatter - Other Vendors

Database - SQL Formatter - Other Vendors


 Shortcut Keys

Shortcut Keys

Versioning – Comment Templates

Versioning - Comment Templates

Versioning - Comment Templates

Finally the ‘File’ menu which mainly allows to export the connections to the source control system you use

File - Export

File - Export

JDev: How to reset or undo a af:table sort

This entry describes how to reset or undo a sort a user has initiated by clicking on a header in an af:table. The use case is based on a question on OTN Jdeveloper forum.
A more detailed description of the case is as follows:

  1. A use has a sortable af:table on a page. At some point he clicks the column header to sort the data.
  2. Now he wants to get back to the unsorted list which he gets is when the table is first shown.

The first part is easily accomplished by setting the sortable property of the columns of the table to true:

<af:table value="#{bindings.EmployeesView1.collectionModel}" var="row"
                          rows="#{bindings.EmployeesView1.rangeSize}"
                          emptyText="#{bindings.EmployeesView1.viewable ? 'No data to display.' : 'Access Denied.'}"
                          fetchSize="#{bindings.EmployeesView1.rangeSize}" rowBandingInterval="0"
                          selectedRowKeys="#{bindings.EmployeesView1.collectionModel.selectedRow}"
                          selectionListener="#{bindings.EmployeesView1.collectionModel.makeCurrent}"
                          rowSelection="single" id="resId1" styleClass="AFStretchWidth"
                          displayRow="selected" binding="#{RTSBean.qtable}">
                    <af:column sortProperty="#{bindings.EmployeesView1.hints.EmployeeId.name}"
                               sortable="true"
                               headerText="#{bindings.EmployeesView1.hints.EmployeeId.label}"
                               id="resId1c1">
...

Part 2 of the case isn’t obvious as there is no ‘unsort’ or ‘reset sort’ button available at design time. To undo the sort you have to do two things:

  1. remove the sort criteria from the table (this will remove the arrows in the column header)
  2. remove the sort criteria from the underlying iterator of the table and execute the query again (this gets the data unsorted from the DB)

Now that we know what to do let’s implement it. You’ll find a link to a working workspace at the end of the article.

public class RTSBean
{
    private RichTable qtable;

    public RTSBean()
    {
    }


    public void resetSort(ActionEvent actionEvent)
    {
        // get the binding container
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
        DCBindingContainer dcBindings = (DCBindingContainer) bindings;
        RichTable table = getQtable();
        table.queueEvent(new SortEvent(table, new ArrayList<SortCriterion>()));
        DCIteratorBinding iterBind = (DCIteratorBinding) dcBindings.get("EmployeesView1Iterator");
        Key currentRow = null;
        Row row = iterBind.getCurrentRow();
        if (row != null)
            currentRow = row.getKey();
        SortCriteria[] sc = new SortCriteria[0];
        iterBind.applySortCriteria(sc); // iterBind is the iterator the table uses
        iterBind.executeQuery();
        if (currentRow != null)
            iterBind.setCurrentRowWithKey(currentRow.toStringFormat(true));
    }

    public void setQtable(RichTable qtable)
    {
        this.qtable = qtable;
    }

    public RichTable getQtable()
    {
        return qtable;
    }
}

I’m using the method resetSort(ActionEvent actionEvent) to do the work. I bound the table to a bean property to make it easily accessible in the method. You can also search for the table component in the component tree (using the id of the table) if you like.
The method first queues a new sort event to the table with an empty list of sort criterion. This removes the sort arrows from the column header. Next it gets the iterator which is used by the table and saves its current row. I do this to re-select the row after I executed the query again. This way the user still sees the selected row after the sort is reset. An empty array of sort criteria is created and applied to the iterator removing all sort criteria currently applied. The query of the iterator gets executed and finally the selected row is set again (if it was set before).

This successfully resets a table to unsorted mode. This picture show the table after an initial search (without criteria entered in the name field):

Unsortet table after first load

Unsortet table after first load

Next I sorted the ‘salary’ column and selected the Row with employee_id 162:

Sorted table with row 162 selected

Sorted table with row 162 selected

The final picture shows the table after a click on the ‘Reset Sort’ button:

Table after 'Reset Sort'

Table after 'Reset Sort'


As you see the row with ‘EmployeeId’ 162 is still selected and scrolled into first position as the table is set to displayRow=”selected”.

The sample workspace uses JDeveloper 11.1.2.1.0 but the bean code should work in older JDeveloper version 11.1.1.x too. The sample used the HR schema as DB connection.
You can download the sample workspace from BlogResetTableSort.zip
After downloading the file rename it to ‘BlogResetTableSort.zip’!

Disclaimer and Acknowledgment: I remember an old blog post by Frank Nimphius showing this too. However, I couldn’t find it on the web and decided to show how to do it again.

JDEV ADF af:goLink: Build Destination URL with Parameter Value from af:inputField

A discussion on the OTN JDeveloper forum asked for an af:goLink which should not be static but use values from form fields as parameters for the link.
I set up a simple workspace with JDev 11.1.2.0.0 to show how to do this. The technique can be used in JDeveloper 11.1.1.x.0 too, only the provided workspace won’t work. To download the workspace see the link at the end of the post.
The workspace uses the HR schema to get access to the employees table and show an employee in a from on the only page.

Running App

Running App


The image shows the running application. You see the form showing an employee, the navigation buttons and the big af:goLink. A click on the link opens a new page with Google and searches for the last name of the employee. When you navigate over the employees the link changes its parameters to pass the last name of the selected employee in the form. To make it more interesting the last name filed is set to autoSubmit so you can search for a changed name without committing the recored first. The af:goLink uses an EL in the destination property to point to a method in a request scope bean which builds the final url with the attached parameter. The parameter is the current value of the last name attribute from the bindings.
Sample 1

Sample 1


Sample 2

Sample 2


Sample 3

Sample 3


Here is the code for the last name input field and the code in a request scope bean to calculate the destination for the af:goLink:

<af:inputText value="#{bindings.LastName.inputValue}"
                                      label="#{bindings.LastName.hints.label}"
                                      required="#{bindings.LastName.hints.mandatory}"
                                      columns="#{bindings.LastName.hints.displayWidth}"
                                      maximumLength="#{bindings.LastName.hints.precision}"
                                      shortDesc="#{bindings.LastName.hints.tooltip}" id="it3"
                                      autoSubmit="true">
                            <f:validator binding="#{bindings.LastName.validator}"/>
                        </af:inputText>
...
                        <af:goLink text="LinkWithParams" id="gl1" targetFrame="_blank"
                                   destination="#{linkWithParams.linkWithParams}"
                                   inlineStyle="font-size:xx-large;"/>
import oracle.adf.model.BindingContext;
import oracle.binding.AttributeBinding;
import oracle.binding.BindingContainer;

public class LinkWPBean {
    public LinkWPBean() {
        super();
    }

    public String getLinkWithParams() {
        // get the binding container
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();


        // get an ADF attributevalue from the ADF page definitions
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("LastName");
        String valLastName = (String) attr.getInputValue();

        String base = "http://www.google.com/#sclient=psy&q="+valLastName;
        return base;
    }
}

You can download the work space here: Workspace BlogLinkWithParams.zip You have to rename the file to BlogLinkWithParams.zip as the file contains a simple zip file.

Using Groovy Expression to set a Primary Key with a Sequence Number

Just set up a workspace for JDeveloper 11.1.2.0.0 to show how to use a Groovy expression to set the primary key of an entity object. Chris Muir blogged about this back in 2009 here ADF BC: Using Groovy to fetch sequence numbers for EO/VO attribute default values. There exists a white paper Introduction to Groovy Support in JDeveloper and Oracle ADF 11g which covers Groovy support in JDeveloper.
So, I don’t cover the basic here but only show how to use this in a sample application. The application uses the HR schema and allows you to insert a new employee. The workspace which you can download (see at the end of this blog) is build using JDeveloper 11.1.2.0.0.
I use the EMPLOYEES_SEQ defined in the HR schema to set the PK of the new employee, EMPLOYEE_ID to the next available sequence number.

Employee Sequence

Employee Sequence


Now we can open the Employee EO from the model layer. Double click on the Employees EO to open the properties inspector for the EO and select the ‘Attribute’ section.
Employees Attributes

Employees Attributes


Now select the EmployeeId in the attributes to get to the attributes properties.
EmployeeId Properties

EmployeeId Properties


Here we can add a default value as literal, expression or as SQL. We select the ‘Expression’ radio button and click on hte pencel on the right side of the input field.
Edit Expression Editor

Edit Expression Editor


Here we enter the Groovy expression to get the next sequence number

(new oracle.jbo.server.SequenceImpl("EMPLOYEES_SEQ",adf.object.getDBTransaction())).getSequenceNumber()

After submitting the dialog with OK you should set the ‘Refresh Expression Value’ to ‘true’ and the ‘Updatable’ LOV to ‘While New’.

Finished Dialog

Finished Dialog


A word of caution here: JDev 11.1.2.0.0 saves the setting for the refresh condition in the xml file, but the next time you open the dialog again the ‘Refresh Expression Value’ value is gone! I’ll file a bug for this later.
This wraps up the the model layer of the app. You can test your work with the Application Module Tester. When you create a new record you’ll see that the EmployeeId is set to the next sequence number.
Oracle ADF Model Tester

Oracle ADF Model Tester


The ViewController project is pretty simple. It consists of an ADF form with with navigation buttons and a button to call a bounded task flow to create the new employee.
Task Flows

Task Flows


The bounded task flow first calls the CreateInsert operation to create a new record which is then displayed in the form. As you notice, the EmployeeId is an af:outputText element, so that you can’t change it.
Running Application

Running Application

The workspace for JDeveloper 11.1.2.0.0 can be downloaded from here: Workspace BlogPKwithGrooy.zip
After downloading the file, remove the suffix ‘.doc’ and rename it to ‘ BlogPKwithGrooy.zip’, as the file is a Zip file.

Extending ViewCriteria to use SQL CONTAINS

Some time ago a customer asked how to use more sophisticated search operands in a ViewCriteria like Oracles CONTAINS or CATSEARCH functions.
CONTAINS and CATSEARCH are part of the Oracle DB 10g and 11g (Enterprise) and offer full text search capabilities (Oracle Text) for columns (even columns containing pdf or word documents in a blob) and external data. As the customer extensively uses full text search the question was how to integrate this functionality in existing and new to develop ADF rich faces applications. As most of the queries are done using view criteria I looked for a way to integrate the CONTAINS search as operand in the ViewCriteria wizard. A thread on OTN I opened for this issue gave some inside hints on how to try to implement this.

Steve Muenchs hint to add the operator directly in the XML definition of the ViewObject (VO) did not work out. The solution I show in this article is a variation of Jobineshs blog. I overwrite the getCriteriaItemClause(…) method to generate the desired SQL clause and add it to the rest of the query. One problem needed to be solved: how to trigger the use of the CONTAINS in the query. As I mentioned before, putting the CONTAINS as operand into the definition of the VO is to cumbersome as it has to be done in every VO where you want to use the CONTAINS search. In the end I used the bind variable to trigger the special treatment.

Each bind variable provides custom properties which I use as trigger. I use the DESCRIPTION property (you can use your own name) and add CONTAINS as value. The overwritten getCriteriaItemClause(…) method checks for each ViewCriteriaItem if its uses a bind variable with the DESCRIPTION set to CONTAINS. If this is the case I generate the SQL clause for the CONTAINS search.

OK, lets walk through the implementation. First I need to create the CONTAINS index which I want to use in the ViewCriteria. I like the index to be case insensitive, so I need to define a preference which is used to alter the index in this way. As a CONTAINS index doesn’t automatically synchronize I change this behavior too, so that I don’t need to do this by hand. For more info about this kind of index check the Oracle Text Reference.

--create a preference to make the index case insensitive
begin
ctx_ddl.drop_preference('bloglex');
end;
/
begin
ctx_ddl.create_preference('bloglex', 'BASIC_LEXER');
ctx_ddl.set_attribute ('bloglex', 'mixed_case', 'NO');
end;
/
-- create the index with sync on commit 
drop index employee_ln_idx;
create index employee_ln_idx on employees(LAST_NAME) indextype is ctxsys.context PARAMETERS('LEXER bloglex SYNC(ON COMMIT)');

A typical SQL query using this index look like

SELECT Employees.EMPLOYEE_ID,
  Employees.FIRST_NAME,
  Employees.LAST_NAME,
  Employees.EMAIL,
  Employees.PHONE_NUMBER
FROM EMPLOYEES Employees
WHERE ( ( ( (contains(Employees.LAST_NAME, 'ha%' ) >0) ) ) );

The syntax I need to generate for a CONTAINS SQL where clause looks like: contains(Employees.LAST_NAME, :bindLN ) >0
I defined a ViewCriteria “ContainsLastNameCriteria” for this

ViewCriteria ContainsLastNameCriteria

ViewCriteria ContainsLastNameCriteria


The definition of the bind variable look like
Bind Variable "bindContainsLastName"

Bind Variable "bindContainsLastName"


As you see I choose ‘Equals’ as operator. It does not really matter what I choose, as the query where clause I build will overwrite it anyway. Instead of the default name “DESCRIPTON” you can choose any other name you like, just overwrite the name field in the dialog.
Now using the technique mentioned in Jobineshs blog I overwrite the getCriteriaItemClause(…) method of the ViewObjectImpl class like:

    ADFLogger mLogger = ADFLogger.createADFLogger(EmployeesViewImpl.class);

    /**
     * Check if a given criteria item holds a bind variable with a property DESCRIPTION set to CONTAINS
     * If yes we build a special where clause part to execute a contains search using the the bind variable as
     * parameter.
     * @param aVCI Criteria item
     * @return where clause part for the criteria item
     */
    @Override
    public String getCriteriaItemClause(ViewCriteriaItem aVCI)
    {
        ArrayList<ViewCriteriaItemValue> lArrayList = aVCI.getValues();
        ViewCriteriaItemValue itemValue = (ViewCriteriaItemValue) lArrayList.get(0);
        if (itemValue.getIsBindVar())
        {
            Variable lBindVariable = itemValue.getBindVariable();
            // check for the special DESCRIPTION in the used bind variable
            Object obj2 = lBindVariable.getProperty("DESCRIPTION");
            String desc = (obj2 != null? obj2.toString(): "null");
            if ("CONTAINS".equalsIgnoreCase(desc))
            {
                if (aVCI.getViewCriteria().getRootViewCriteria().isCriteriaForQuery())
                {
                    // normal query execution
                    return getCONTAINSClauseForDatabaseUse(aVCI);
                }
                else
                {
                    // for in memory we don't need to anything so just return '1=1'
                    return "1=1";
                }
            }
            else
            {
                // no special treetment for all other CriteriaItems
                return super.getCriteriaItemClause(aVCI);
            }
        }
        return super.getCriteriaItemClause(aVCI);
    }

    protected String getCONTAINSClauseForDatabaseUse(ViewCriteriaItem aVCI)
    {
        ArrayList<ViewCriteriaItemValue> lArrayList = aVCI.getValues();
        ViewCriteriaItemValue itemValue = (ViewCriteriaItemValue) lArrayList.get(0);
        String whereCluase = "1=1";
        if (itemValue.getIsBindVar())
        {
            Variable lBindVariable = itemValue.getBindVariable();
            Object objVarVal = ensureVariableManager().getVariableValue(lBindVariable.getName());
            String varVal = null;
            if (objVarVal != null)
            {
                varVal = objVarVal.toString();
            }
            else
            {
                // if no parameter given we search for all
                varVal = "%";
            }

            String bindVarName = lBindVariable.getName();
            // can use entiy name only if VO is based on an EO
            String entityName =
                (this.getEntityDefCount() > 0? this.getEntityDef(0).getAliasName() + ".": "");
            whereCluase =
                    "(contains(" + entityName + aVCI.getColumnName() + ", :" + bindVarName + " ) >0) ";
        }
        mLogger.fine("Build clause: " + whereCluase);
        return whereCluase;
    }

That’s about it for the code, lets look at the running sample which you can download from here BlogContainsSearchCriteria.zip. Remove the ‘.doc’ suffix after downloading the sample work space for JDeveloper 11.1.2 as it contains a normal Zip archive.
After the start of the sample app you see a normal QueryPanel with table component which I dragged fro mthe DataContron onto a page fragment. Select ‘ContainsLastNameCriteria’ from the search select list and enter e.g. ‘ha%’ into the LastName input field.

First run using ContainsLastNameCriteria

First run using ContainsLastNameCriteria

The query returns all last names starting with ‘ha’, but also the last name ‘De Haan’ where the ‘ha’ starts the second part of the last name. This is normal expected behavior. This is the output in the log window:

[368] SELECT Employees.EMPLOYEE_ID,
  Employees.FIRST_NAME,
  Employees.LAST_NAME,
  Employees.EMAIL,
  Employees.PHONE_NUMBER,
  Employees.HIRE_DATE,
  Employees.JOB_ID,
  Employees.SALARY,
  Employees.COMMISSION_PCT,
  Employees.MANAGER_ID,
  Employees.DEPARTMENT_ID
FROM EMPLOYEES Employees
WHERE ( ( ( (contains(Employees.LAST_NAME, :bindContainsLastName ) >0) ) ) )
[369] Bind params FOR ViewObject: [de.hahn.blog.containscriteria.model.dataaccess.EmployeesView]BCSCAppModule.EmployeesView1
[370] Binding param "bindContainsLastName": Ha%

Here we see the generated query using the CONTAINS index in the where clause. You may argument that this same result could archived using the normal ‘contains’ operator’ available in the ViewCriteria wizard. The generated where clause in this case looks like

... WHERE ( ( (UPPER(Employees.LAST_NAME) LIKE UPPER('%' || :bindLastName || '%') ) ) )

The big difference is the usage of ‘UPPER’ and the ‘%’ in front and after the bind variable. The usage of ‘UPPER’ is the result of selecting ‘Ignore case’ in the view criteria editor. One disadvantage of using UPPER is, that if you have defined an index on the column (LAST_NAME in this sample), it can only be used if it was created with the UPPER function too. For small data volumes it’s OK to use a query like this without an index, but if you query big volume data it’s an absolute (time) killer.
Next the usage of ‘%’ before the bind variable is problematic too fro big volume data, as the DB needs to read each value to match it against the expression. The result also differs from the desired as names like ‘Kochhar’ are found too.

Normal 'contains' criteria - Wrong result

Normal 'contains' criteria - Wrong result


The very popular web side Ask Tom has plenty of threads discussing this.

The sample workspace contains this criteria and a mixed criteria to show that you can mix normal criteria and the special criteria in one criteria group. Feel free to play with these cafeterias too.

The technique shown here can be used for almost any other SQL where clause which is not part of the ViewCritera editor (e.g. you want to call a pl/sql function as part of the ViewCriteria).

Three ways to delete a row while navigate over a RowSet

An OTN user ask how to delete a row while navigation a row set. This question and the problem the user got with implementing this leads to this sample. Shay Shmeltzer mentioned a pending bug #12671112 in JDev 11.1.2 which may cause trouble deleting a row while navigation a row set. As far as I understand only the first method (direct delete via data control) is affected by the bug.

The are a couple of ways to archive this task. This sample uses the declarative way of doing this. So no bean code is involved. I’m using the HR schema and the departments table to show the techniques. Be sure to save the content of the DEPARTMENTS table as the deletion will remove some rows if you commit the operation. For this you can e.g. export the table content as SQL insert statements to a file.

You can download the sample code from here
You need to remove the suffix ‘.doc’ as the file itself is a zip archive.

The sample shows three approaches to the problem, all declarative.
First I use the ‘Delete’ method from the DataControl ViewObject to delete the currently selected row.
The second approach uses a custom method in th e VO which gets the department id to remove as a parameter.
Finally the third approach uses a task flow method call to the above mentioned method to archive the deletion of the row which is identified by the passed department id.

Final application

Final application

The application consists of a ADF form together with the navigation buttons, commit and rollback button and one button to activate each of the three solutions.

1. Solution

The delete button is bound directly to the ‘Delete’ method of the VO of the DataControl:

<af:commandButton actionListener="#{bindings.Delete.execute}"
    text="Delete" immediate="true" id="cb9"/>

and the binding

Delete Binding

Delete Binding


As mentioned above the solution might cause trouble. For more info read this thread.

2. Solution

The second approach uses a custom method which is implemented in the DepartmentViewObject. The method receives the department id to delete as parameter, uses a viewCriteria to find the row and deletes the row if found. The method is exposed in the client interface of the VO to make it accessible on the UI layer.

    /** Delete the department row with the department id aDepId
     * Search the department via its id and if found delete the row without committing
     * @param aDepId Id of the department to delete
     */
    public void deleteDepartmentById(Number aDepId)
    {
        this.setWhereClauseParams(null);
        this.setWhereClause(null);
        this.setbindDepId(null);
        ViewCriteria lCriteria = this.getViewCriteria("DepartmentByIdCriteria");
        removeApplyViewCriteriaName(lCriteria.getName());
        this.applyViewCriteria(lCriteria);
        this.setbindDepId(aDepId);
        this.executeQuery();
        Row row = this.first();
        if (row != null)
        {
            row.remove();
            mLogger.info("Row with DepId " + aDepId.toString() + " deleted (not committed yet)!");
            removeApplyViewCriteriaName(lCriteria.getName());
            this.executeQuery();
        }
        else
        {
            mLogger.info("Row with DepId " + aDepId.toString() + " not found!");
        }
    }

The button ‘deleteDepartmentById’ binds this method to the button

<af:commandButton actionListener="#{bindings.deleteDepartmentById.execute}"
    text="deleteDepartmentById"
    disabled="#{!bindings.deleteDepartmentById.enabled}"
    id="cb7" immediate="true"/>

and the binding of the method

Binding DeleteDepartmentById

Binding DeleteDepartmentById


In this case the selected department id is passed to the method directly in the binding dialog (which is opened when you drop a method which needs a parameter onto the page:
Edit Action Binding of DeleteDepartmentById

Edit Action Binding of DeleteDepartmentById

3. Solution

The final approach used the same method mentioned in the 2nd approach, but uses a task flow method call to execute it.

Task Flow Method Call

Task Flow Method Call


The Button uses a af:setPropertyListener to pass the selected department id to the method in pageFlowScope variable ‘#{pageFlowScope.depId}’

<af:commandButton text="Delete Row via TaskFlow" id="cb8"
    action="delete" immediate="true">
    <af:setPropertyListener from="#{bindings.DepartmentId.inputValue}"
        to="#{pageFlowScope.depId}"
        type="action"/>
</af:commandButton>

The task flow method call was dropped from the dataControl onto the bounded task flow. The picture below shows the pageDef file which was generated by the framework:

PageDef of Method in TaskFlow

PageDef of Method in TaskFlow


This time the parameter is read from the pageFlowScope variable ‘#{pageFlowScope.depId}’.

All three solutions are doing the same in the end: they delete a row from a row set. Solutions 2 and 3 can be used when the table you use to select the row is based on an other (e.g. read only) VO then the VO you use to delete the row.
The same technique can be used for other methods defined in the VO too, e.g. to select a row in a read only table and edit the row in a ADF form (using a method which find the row ba id).

Using diferent VOs for Master Detail Navigation (the Declatative Way)

A user on the OTN forum asked a question how to do a master detail like navigation where the master VO is not equal to the detail VO and no accessors or link is available between the tow VOs.

A use case for this scenario is e.g. you have a read only table as master which holds an attribute which is the foreign key to an other table (the master table has a FK to the detail you like to change). In the sample I’m talking about in this blog I used the HR schema, the employees table as master and the the department as detail. I show how to use the employees as read only table, select an employee to edit the department the employee is assigned to.

Here is the data model of the sample:

Data Model

Data Model


As you can see there are no view links defined which could be used to navigate from the employee to the related department.

I’ll do all this the declarative way, so I don’t use a bean or other Java code. I use a bounded task flow and start with a query panel with the read only employees table. Each row shows the id of the employee, the name and the department id. I add a button to the department id of each row and use this to navigate to the departments edit page. Here you see the running app, the query panel which I used to select employees records and the button which I added to the department is column.

Start Screen

Start Screen

I used a button here because of an error in this version (11.1.2) of jdev which prevent the table from selection the current row when you just hit a link in a row. Frank Nimphius provided a workaround for this here:JDeveloper 11.1.2 : Command Link in Table Column Work Around. A click on the ‘Department’ button for ‘Jannette King’ will navigate to Department ’80’ which is editable

Select a Department from the Table and Edit Department

Select a Department from the Table and Edit Department

The work flow is implemented as shown below:

Work Flow

Work Flow

As you see the whole work is done in bounded task flow which first presents the query panel together with the resulting employees table (read only). The column ‘Department ID’ shows the button I use to navigate to the editable departments page. As there is no view link, it’s not enough to select the employees row to mark it as current row. I have to extract the department id from the selected row and use this to search for the department before showing the departments edit page.

I store the department id in a page flow scope variable named ‘#{pageFlowScope.depKey}’. If you like you can store the value of the department id elsewhere e.g. in the variables iterator of the page binding. To extract and store the value I use a af:setPropertyListener which allows to react on the action of the button and transfer the value to page flow scope variable. Here is the code of the department id column:

                            <af:column sortProperty="#{bindings.EmployeesView1.hints.DepartmentId.name}"
                                       sortable="true"
                                       headerText="#{bindings.EmployeesView1.hints.DepartmentId.label}"
                                       id="resId1c4" width="114">
                                <af:outputText value="#{row.DepartmentId}" id="ot5">
                                    <af:convertNumber groupingUsed="false"
                                                      pattern="#{bindings.EmployeesView1.hints.DepartmentId.format}"/>
                                </af:outputText>
                                <af:commandButton text="ShowDepartment" id="cb2" action="showDep">
                                    <af:setPropertyListener from="#{row.DepartmentId}"
                                                            to="#{pageFlowScope.depKey}"
                                                            type="action"/>
                                </af:commandButton>
                            </af:column>

The button action navigates to he method call ‘SetCurrentRowWithKeyValue’ in the bounded task flow. This method I dragged from the data control palette from the DepatermetnsView1 operation onto the bounded task flow definition page

SetCurrentRowWithKeyValue from DepartmentsView1

SetCurrentRowWithKeyValue from DepartmentsView1

The method searches the department using the the value stored in the page flow scope variable. The dialog below opens automatically when you drop the method on the task flow and lets me enter the key value to search for:

setCurrentRowWithKeyValue  Edit Action Binding

setCurrentRowWithKeyValue Edit Action Binding

Here is the pagedef file for the method call:

PageDef of setCurrentRowWithKeyValue Method

PageDef of setCurrentRowWithKeyValue Method


After the search the current row is set in the DepaertmensView1 and I can navigate to to the edit page. That’s about it.

You can download the sample work space from here Sample Workspace blogmasterdetaildeclarative_v2-zip. You have to rename the file to ‘.zip’ after download!

Validate Data before Export via af:exportCollectionActionListener or af:fileDownloadActionListener

ADF rich faces offer a nice and easy feature to stream data to the client (e.g. Excel) using the af:exportCollectionActionListener or af:fileDownloadActionListener component. Both of the components get the output stream from the response, so the application an add the data.
One problem is that the two components fire before the application has a chance to validate other data on the page or do some other needed work.
To overcome this shortcoming you can implement the trick I’m outlining in this blog. The idea is to use a normal af:commandbutton or af:commandToolbarButton on the page which calls an actionListener in a bean. In there you can validate other page data or do some other stuff in the model, and then queue an event inside the bean method to call another invisible button on the page which has the af:exportCollectionActionListener attached.

As it turned out (thanks Jiandong Xin for pointing it out), there is a problem if the button we queue the action event to has a client behavior tag attached. This is the case for af:exportCollectionActionListener or af:fileDownloadActionListener. These tags don’t work properly if we only queue an event to the parent button. As Jobinesh pointed out in his blog we need to insert a JavaScript function into the page which handles the event queuing from JavaScript and call it from the bean. This way we are able to set the needed parameters (e.g. not to wait for a response).

Now lets implement this: Lets start with a toolbar button which resides on a panel collection holding the table we want to export. The button has a binding to the bean (binding=”#{exportBean.exportCollectionButton}”) to make it easier to call it from the bean. If you don’t like to bind the button to the bean, you can search for the component by name inside the action listener we implement below. The page contains the custom handler (customHandler(event)) which we call from the bean method. This handler then queues the event to activate the button which starts the file download. The connection between the customHander and hte commandToolbarButton is the ID of the commandToolbarButton (“ctb1”). You have to change this if your button has a different ID.

<af:resource type="javascript">
  function customHandler(event){
    var exportCmd = AdfPage.PAGE.findComponentByAbsoluteId("ctb1");
    var actionEvent = new AdfActionEvent(exportCmd);
    actionEvent.forceFullSubmit();
    actionEvent.noResponseExpected();
    actionEvent.queue();
  }
</af:resource>
<af:panelCollection id="pc1">
  <f:facet name="menus"/>
    <f:facet name="toolbar">
      <af:toolbar id="t2" binding="#{exportBean.toolbar}">
        <af:commandToolbarButton text="Export..." id="ctb1"
            binding="#{exportBean.exportCollectionButton}"
            visible="false">
          <af:exportCollectionActionListener type="excelHTML" exportedId="t1"
              title="Export"/>
        </af:commandToolbarButton>
...

The vital part is that the visible property is set to “false”, so that the button will not show up on the page, but is fully functional. Somewhere else on the page (or even on the same toolbar) we have an with an action listener pointing to a bean method (exportBean in the sample). This button is visible and used to initiate the export.

...
<af:commandButton text="export via bean" id="cb8" 
    actionListener="#{exportBean.exportprgActionListener}"/>
...

The exportBean actionListener looks like this

    // toolbar button with export listner attatch bound from page
    private RichCommandToolbarButton exportCollectionButton;
...
    public void exportprgActionListener(ActionEvent actionEvent) {
        // Add event code (validation or other) here...

        // queue the event via a JavaScript inserted into the page
        FacesContext context = FacesContext.getCurrentInstance();
        ExtendedRenderKitService erks =
        Service.getService(context.getRenderKit(), ExtendedRenderKitService.class);
        erks.addScript(context, "customHandler();");
    }

In the action listener above you can validate other data on the page or call service methods in your application module before starting the export, or cancel the export if you like. The export runs as if you click the af:exportCollectionActionListener directly.
You can download a sample workspace BlogFileDownLoadTest.zip
After download remove the ‘.doc’ suffix!

JDev: Always Test Your App with ApplicationModule Pooling turned off

In the last couple of weeks I saw a couple of question which mentioned a sporadic misbehavior of the application under different circumstances. In the end they could be answered:

“Your application has not been tested with application module pooling turned off”

Whenever you encounter a sporadic misbehavior of the application under different circumstances, this should ring a bell. Most developers came across a situation like this when programming ADF applications. At some point the application does not react normal or throws exceptions. The errors are not reproducible (most of the times) and you only see them on the production server (never on the developer machine).

The first time I came across this problem it took me about a week to figure it out and solve it. Basically the problem has been private data (stored with the session) which is part of the application module and is stored in the user data hash map. This is not a problem as long as you can guarantee that each user always works with the same application module. This is the case when you test run your application on the developers machine (you are the only user and the application module pool always returns the same application module to the client). On a production server where more then one user uses the application at the same time, the application may be forced to reuse an application module which was formally used by a different user. At this point application module pooling take over.

The general algorithm used is that the application module pool has a number of application module available to use. If more requests arrive the pool generates the additional module until a high water mark is reached. Further requests getting rejected. Currently not used modules are given back to the pool and are available for the next request. The pool tries to return the same application module as long as it’s available to the same session for subsequent requests. If it is not available it uses a currently available module stores the current status of the module into a store (DB or file), clears the module from all information and reconstructs the state from the other session from saved state information. Once the application module is restored you can’t distinguish if it’s a new application module or a reused one. This way your application don’t need an module for each user request, but it shares the available modules between the requests, saving lots of resources.

All this can be read about here 43.2 Introduction to Fusion Web Application State Management.

After this more theoretical prologue, lets do a practical project (workspace for JDev 11.1.2 available, see end of article). To make it as simple as possible, but still useful for anybody running into problems with activation/passivation, we use the HR schema and try to emulate a scenario where a user only sees employee data which depends on a department number. This department number should be set in the application module and be accessible for all queries. In a real world scenario this information is connected to the login of a user and stored in a central place. In the sample we use the user data of the application module to store the number.
The applications start page (I do spare the login part) has a af:query panel to select employees which might be filtered by their last name. As there is no login I added a field to enter the department number which should be used to further filter the result set.The Web UI lokke like

Test app UITest Application UI

As you see there is an input field for the last name, in the bottom era an input field to insert the department number and an other panel to retrieve the currently set department number from the application module.
Lets have a look at the service method to get/set the department number and how it is stored:

    private static final String PRIVATEDATA = "privData";

    public void setPrivateToUserData(String aVal) {
        mLogger.info("Set PrivData:" + aVal);
        if (aVal == null)
            return;

        Session lSession = getSession();
        if (lSession == null) {
            mLogger.warning("getSession returned null!");
            return;
        }
        Hashtable lHashtable = lSession.getUserData();
        if (lHashtable == null) {
            mLogger.warning("getUserData returned null!");
            return;
        }
        lHashtable.put(PRIVATEDATA, aVal);
    }

    public String getPrivateToUserData() {
        Session lSession = getSession();
        if (lSession == null) {
            mLogger.warning("getSession returned null!");
            return null;
        }
        Hashtable lHashtable = lSession.getUserData();
        if (lHashtable == null) {
            mLogger.warning("getUserData returned null!");
            return null;
        }
        String lData = (String) lHashtable.get(PRIVATEDATA);
        mLogger.info("Get PrivData: " + lData);
        return lData;
    }

As you see the string from the UI is stored and retrieved under the key PRIVATEDATA = “privData” in the userData hash map of the application module. This is the place to store data for the current user session (Storing Information About the Current User Session)
The configuration of the application module is the default you see after generating a ‘Fusion Web Application’.

ApplicationModule Default Configuration

ApplicationModule Default Configuration


As you can see ‘Application Module Pooling’ is on. When we run the application, set the department number to e.g. 30, send the data to the AM and hit the search button in the query panel we see
Pooling On

Pooling On


You can hit the edit button in a row to edit the employee and come back to the page and see the application running as expected.
Now, we switch ‘Application Module Pooling’ off and run again:
Application Module Pooling Off

Application Module Pooling Off


Doing the same actions as earlier it look like the application does not see the department number at all:
Application Module Pooling Off

Application Module Pooling Off


Even if you set the department number and directly hit the ‘Get User Data’ button you’ll don’t get the department number back.

The reason for this behavior is that we store the department number in the user data hash map which is NOT passivated when the application module is given back to the pool and given to an other requester. This happens every time you go the the server when am pooling is switched off.
What we need to to is to passivate the session user data together with the other state data stored by the framework and load it back when the AM is requested the next time (when it gets activated again). To do this we have to overwrite two methodes in the ApplicationModuleImpl class.

    @Override
    protected void activateState(Element aElement) {
        super.activateState(aElement);
        mLogger.info("++++++++++ activateState");

        Hashtable lData = getSession().getUserData();
        if (aElement != null) {
            // 1. Search the element for any <PrivData> elements
            NodeList nl = aElement.getElementsByTagName(PRIVATEDATA);
            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 <PrivData> element
                    Node child = nl.item(i).getFirstChild();
                    if (child != null) {
                        // 4. Set the data value to the user data hashmap
                        String lDataString = child.getNodeValue();
                        String[] lSplitkeyval = lDataString.split(";");
                        for (int ii = 0; ii < lSplitkeyval.length; ii++) {
                            mLogger.fine("..."+lSplitkeyval[ii]);
                            String[] lSplit = lSplitkeyval[ii].split("=");
                            lData.put(lSplit[0], lSplit[1]);
                        }
                        break;
                    }
                }
            }
        }
    }


    @Override
    protected void passivateState(Document aDocument, Element aElement) {
        super.passivateState(aDocument, aElement);
        mLogger.info("---------- passivateState");

        // 1. Retrieve the value of the user data to save and build a string representation
        Session lSession = getSession();
        Hashtable lData = lSession.getUserData();
        String lDataString = "";
        Set<String> keyset = lData.keySet();
        if (!keyset.isEmpty()) {
            Iterator<String> keys = keyset.iterator();
            while (keys.hasNext()) {
                String key = keys.next();
                mLogger.fine("..."+key + "=" + lData.get(key));
                lDataString += key + "=" + lData.get(key) + ";";
            }
        }

        // 2. Create an XML element to contain the value
        Node node = aDocument.createElement(PRIVATEDATA);
        // 3. Create an XML text node to represent the value
        Node cNode = aDocument.createTextNode(lDataString);
        // 4. Append the text node as a child of the element
        node.appendChild(cNode);
        // 5. Append the element to the parent element passed in
        aElement.appendChild(node);
    }

The passivateState method gets a Document and an Element as parameter. After calling super() to let the framework do its work, we get the user data hash map and store each key-value pair in a string which is then appended as a node to the element we got as parameter. In a real world application I would use a java to XML serialization tool like XStream which is capable to store more complex data.
The activateState method gets an Element as parameter which we search for the node we save when passivateState was called and restore the user data hash map.

After putting the two methods in the ApplicationModuleImpl class (the one you get when you create the java classes for an application module) the application run OK again, application module pooling still turned off.

When you examine the sample workspace which you can get here BlogActivatePassivateSample_V1.zip (remove the ‘.doc’ extension after downloading the workspace!) you’ll notice, that EmployeesViewImpl class too have the two methods to store private data which is not automatically saved by the framework. Further there are log messages throughout the code to let you follow the action in the log window.

As you see it’s essential to test an application with application module pooling turned off to find activation/passivation errors before the application goes to production.