Remove Automatic Component Bindings

The last couple of day I spend some time investigating the ‘Automatic Component Binding’ feature. A code review I did lately showed that about 40% of the jspx pages of one project (about 60 pages in total) had automatic component binding on. As each page thus have a bean defined where the ui components are bound to, the project also contains a lot of beans. The beans are generated automatically and in this case don’t do anything but eat memory.

So two questions came up:
1) Why where automatic component binding on in so many pages, as we don’t use this feature?
2) How to get rid of the bindings and the (for us) useless beans?

This blog entry answers the second question and at least gives one hint for the first one. Lets start with the second question.

Reading the documentation Oracle┬« Fusion Middleware Web User Interface Developer’s Guide for Oracle Application Development Framework shows how to turn on/off the automatic component binding via JDeveloper for a page or fragment

Automatic Component Binding On/Off

Automatic Component Binding On/Off


If you turn the automatic binding on you’ll notice a comment line in your jsxp file

</f:view>
  <!--oracle-jdev-comment:auto-binding-backing-bean-name:backing_view11-->
</jsp:root>

This entry in the file triggers the automatic binding. Removing it has the same effect as turning the feature off in the above dialog.
So, as the doc stated, turning the automatic binding off only has an effect for components which are put onto the page in the future. If your page contains e.g. one or more forms, there are many components which are bound needlessly to a bean. It’s an error prone task to go through the source and remove all the bindings properties by hand.
JDeveloper allows us to use regular expressions in search and replace operations (even in search and replace in files). Looking to the source of the jspx page reveals

<f:view>
    <af:document id="d2" binding="#{backingBeanScope.ffff.d2}">
      <af:messages id="m2" binding="#{backingBeanScope.ffff.m2}"/>
      <af:form id="f2" binding="#{backingBeanScope.ffff.f2}">
        <af:panelStretchLayout topHeight="50px" id="psl2"
                              binding="#{backingBeanScope.ffff.psl2}">

that the automatic bindings all are looking like ‘binding=”#{backingBeanScope.ffff.<id>’, so we only need a regular expression looking for this string and replace it with nothing.

Replace Dialog

Replace Dialog


Running the dialog above shows that the expression finds the desired targets
Running Replace

Running Replace


After all references to the bean have been deleted, we can simply delete the bean itself. This concludes the answer to the second question.

The answer to the first question is not that easy to give. First of all I found out, that if you once turned the automatic component binding on, it stays on for all other pages you put into the same task flow. It may stay on for all pages, but I couldn’t confirm this in all cases. At some time a developer turned this feature on in the dialog you see when you create a new page (or fragment) in the bounded task flow

Create Page Dialog

Create Page Dialog


You have to click the ‘+’ sign by ‘Page Implementation’ to see the option. The thing is, the next time you create a new page the ‘Page Implementation’ is disclosed and you have to open it to see the current state. As I mentioned before, the option keeps its setting in the task flow. This answers the question of how we end up with so many pages using this feature. A developer turned this on for some reason and all pages added to the task flow later on inherited the behavior.
One interesting thing to note is, that you don’t see the ‘Bindings’ property for components, if you have the automatic component binding turned on
Bindings visible - Automatic Component Binding Off

Bindings visible - Automatic Component Binding Off


Binding invisible - Automatic Component Bindings On

Binding invisible - Automatic Component Bindings On

A summery of this is that you should only use the ‘Automatic Component Binding’ feature if you really need it. If you turn it on, make sure to check its status often and turn it off when not needed.

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).

How to find and reset the system11.x.x.x folder for a JDeveloper installation

Lately users on the OTN forum asking how to change the system configurations or re install a specific version of JDeveloper when some undefined error persists.

Often the questions can be answered by rename or delete the system11.1.x.x.x folder which holds the system configuration and the integrated Weblogic Server (WLS) in a sub directory (DefaultDomain). If the problem only affects the integrated WLS you can just delete the DefaultDomain folder. The next time you run or debug an application the WLS DefaultDomain will be recreated.
The same is true for the system11.x.x.x folder with one difference. When JDeveloper detects an other system11.x.x.x folder on your system, it asks you if you want to migrate from the other version. In this dialog you can select or search (if you click the magnifying glass) a version to migrate from. You can force the dialog to appear if you start JDeveloper with the option ‘-migrate’ from a command shell.

Systemfolder11.1.1.4.0JDeveloper Migrate

Migrate system folder

You should be careful with the migration as it tends to migrate any error too. So if you have to delete the system11.x.x.x folder because of an error, you should first rename the folder, answer the dialog with ‘No’ and see if the problem is solved. Then you can delete the freshly created system11.x.x.x folder and try again with migrating from the renamed folder. If you are lucky the error will not be migrated. After any such action you should check the preferences again, as all changes you made have been erased.

By default JDeveloper put the system11.x.x.x folder in the “USER_HOME\AppData\Roaming\JDeveloper\” Folder (windows), as long as you did not specify an other location by changing an environment variable or changing the jdev.boot file.

#
# The ide.user.dir.var specifies the name of the environment variable
# that points to the root directory for user files.  The system and
# mywork directories will be created there.  If not defined, the IDE
# product will use its base directory as the user directory.
#
ide.user.dir.var = JDEV_USER_HOME,JDEV_USER_DIR
ide.user.dir = C:/Oracle/Middleware/jdeveloper/system

or specify the it as startup parameter

<path_to_jdev_install>\jdeveloper\jdeveloper.exe -J-Dide.user.dir=c:\JDeveloper\11.1.1.4

I find it helpful to use non-default locations for the JDeveloper user directory (which will contain the system.x.y.z folder) – in fact I find it so helpful, that I usually have several launcher shortcuts for the same version of JDeveloper pointing to different user directories (instead of specifying it in jdev.boot).
However, if you have many different launcher shortcuts I sometimes loose track which I’m currently running.

Well, there are a couple of ways to find out the location from within JDeveloper:
1) When you start an application in the internal WLS you get the path in the log window as part of the deployment messages

<13.08.2011 15:41 Uhr MESZ> <Notice> <WebLogicServer> <BEA-000360> <Server started in RUNNING mode> 
IntegratedWebLogicServer startup time: 26690 ms.
IntegratedWebLogicServer started.
[Running application xxyyzz on Server Instance IntegratedWebLogicServer...] 
[03:41:44 PM] ----  Deployment started.  ----
[03:41:44 PM] Target platform is  (Weblogic 10.3).
[03:41:45 PM] Retrieving existing application information
[03:41:45 PM] Running dependency analysis...
[03:41:45 PM] Deploying 2 profiles...
[03:41:47 PM] Wrote Web Application Module to P:\jdeveloper\system\system11.1.1.4.37.59.23\o.j2ee\drs\xxyyzz\xxyyzzViewControllerWebApp.war
[03:41:48 PM] Wrote Enterprise Application Module to P:\jdeveloper\system\system11.1.1.4.37.59.23\o.j2ee\drs\xxyyzz
[03:41:48 PM] Deploying Application..

2) you open the menu ‘Help’->’About’ and select the ‘Properties’ tab. Depending on the version you find multiple entries in the properties section.

Systemfolder11.1.1.4.0JDeveloper

System folder JDeveloper 11.1.1.4.0


and for JDeveloper 11.1.2.0.0
Systemfolder11.1.2JDeveloper

System folder JDeveloper 11.1.2

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).