Saturday, May 29, 2010

EL to see whether a view activity is authorized for the current user

You can use the below EL to see whether a specific view activity is authorized for the current user

#{controllerContext.security.activity['your_view_activity_id'].viewAuthorized}

This is useful where you need to skip the rendering of links that refer to a page which is not authorized to view by the current user.

Please refer ControllerSecurityManager api to learn more.

You can download the sample workspace from here.
Run the login.html. Login to application as as guest/welcome123, application displays only 'Department' link. If you login as admin/welcome123, then application displays both 'Department' and 'Employee' links.
[Runs with Oracle JDeveloper 11g R1 PS2 + HR Schema]

Friday, May 28, 2010

Conditional rendering of editor components for a clickToEdit table

A very common use case related to table with 'click-to-edit' mode is to render some custom editor component for some cells, when user selects a row for edit. Newly added (as part of PS2 release) 'activeRowKey' attribute on table component will help us here.

The 'activeRowKey'is the row that is currently active on the client. In click-to-edit mode, the active row will be made editable and is brought into view (if not already visible). Upon initial display, the click-to-edit component defaults the active row to the first visible row.


A bit about the implementation. Please see the below given jsf tag snippet, which may help you to understand the concept better. Here, the EL 'backingBeanScope.TestBean.tbl.rowKey eq backingBeanScope.TestBean.tbl.activeRowKey' checks whether current row is the activeRowKey, and based on that display is flipped between components

<af:column sortProperty="DummyAttribute" sortable="true"
  headerText="#{bindings.DepartmentsView11.hints.DummyAttribute.label}"
  id="c2">
    <af:switcher id="s1" defaultFacet="InActive"
       facetName="#{TestBean.tbl.rowKey eq TestBean.tbl.activeRowKey ? 'Active':'InActive'}">
        <f:facet name="InActive">
            <af:outputText value="#{row.bindings.DummyAttribute.inputValue}"
                               id="it3"/>
        </f:facet>
        <f:facet name="Active">
            <af:commandButton text="I'm Dummy" id="cb1"
                                  partialSubmit="true"/>
        </f:facet>
    </af:switcher>
 </af:column>

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS2 + HR Schema]

Thursday, May 20, 2010

What you may need to know about oracle.adf.view.rich.automation.ENABLED flag ?

You can use oracle.adf.view.rich.automation.ENABLED in web.xml file to enable automation testing.
<context-param>
      <param-name>oracle.adf.view.rich.automation.ENABLED</param-name>
      <param-value>true</param-value>
</context-param>

But you may need to be measured while setting this parameter and it's advised to keep this parameter disabled in production environment.

Why?

Because enabling the test automation parameter generates a client component for every component on the page which can negatively impact performance.
1. Result in increased page size
2. Involves in 'n' number of checks(assertions) at run time.

Now, let me share a couple of interesting observations related to this parameter.

Few teams reported saying that after enabling this parameter, some web pages started misbehaving. Most of the issues were related to popup dialog, like popup (Custom popup, List Of Value, Datepicker etc) dialog no longer respond to user actions, started throwing assertion failed errors "Incorrect use of AdfRichUIPeer.GetDomNodeForCommentComponent.AdfRichPopup" etc.

Hey...So Is this parameter that bad ? No :)

All these issues were caused by wrong code. Enabling 'automation' actually brought them to light. Enabling 'automation' in turn enables Assertion, so the system throws assertion failed errors whenever it encounters incorrect usage of components. Root cause for this error is invalid DOM tree on the client side. Current implementation doesn't communicate the exact cause while throwing assertion error, but this is getting arrested in next release.

Coding Errors

Listing some of the coding errors that caused the wrong view/page behavior when automation is enabled(shared by different teams).

1. Missing Iterator binding : Some controls were referring iterators which were missing from the page definition. If the assertion is not enabled, this error is silent and simply caused the component referring the missing iterator not to render.

2. Keeping non-rendered(rendered=false) components as partial trigger component. Work around is to use visible=false.
For example,the following jsf snippet causes Assertion failure error "Assertion failed: Incorrect use of AdfRichUIPeer....", because of the above stated reason.
<af:commandButton  id="hiddenBtn" rendered="false" text="Test"/>
<af:table var="row" id="t1"  partialTriggers="::hiddenBtn">

3. Improper nesting of tags.
For example...

The below hierarchy is wrong. Because Rich Client
events will bubble up to component handlers, 
so listeners should be enclosed properly within
the source component that originated the event
(Thanks to Gary Van Matre for explaining this behavior).  

<af:commandLink 
    <af:showPopupBehavior
        <af:image
        <af:clientListener 

Correct usage

<af:commandLink 
    <af:image
       <af:showPopupBehavior
       <af:clientListener 

Internationalizing the display name of train stop

There is an interesting post by Duncan on decorating the ADF train component. If you want to localize the display name of train stop, you can try out the below EL.
#{adfBundle['Your ResourceBundle'].SomeKey}

Saturday, May 15, 2010

Displaying an empty table !

The title sounds silly, right? Whats the big deal in this? Yes you are right, this post discusses a very simple 'tip'. The use case requirement here is a pretty simple one where a table in a data capture page should be displayed empty on some conditions. Annoyingly, a table created by dragging and dropping a ViewObject on a page always display data. Hmm...this is very much expected, as and when the table renders, table model will try to get data though the binding by executing the ViewObject.
How to prevent this execution? Let me ask you a counter question, How does a search screen with result table displays empty result table initially?
A simple us case triggers lots of questions :) The answer for the above question end up in search binding's 'AutoQuery-or-ClearRowSet'. This is explained in one of my previous post.

Clearing RowSet

Lets us focus on 'ClearRowSet' behavior in this post. ClearRowSet in turn calls ViewObjectImpl::executeEmptyRowSet(). This method actually mark the ViewObject as executed and present the caller with empty row set , without actually triggering a database query. If you really want to know more about this method, please check out this post What Does executeEmptyRowSet() Do? from Steve Muench

Oh, cool...Can we use this strategy for the above use case as well?
Answer is YES.
Define an ApplicationModule method that calls ViewObjectImpl::executeEmptyRowSet(), drag and drop this as method call activity on you navigation definition( adfc-config.xml / your-task-flow-definition.xml) and then define a control flow connecting between caller and callee views(callee is supposed to display the empty table) with the above defined method call activity as an intermediate step. Control flow case looks like as shown below

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS2 + HR Schema]

Tuesday, May 11, 2010

Fine tune your ADF application

The chapter 7 Oracle Application Development Framework Performance Tuning from Oracle® Fusion Middleware Performance and Tuning Guide conatins very usefult tips to fine your enterprise applications built using Oracle Application Development Framework. Check it out!

CRUD operations on a tree table

<af:treeTable> is useful to present hierarchical data to the business users. ADF BC layer got a good support for use cases with tree table components. I'm sharing a simple application illustrating CRUD(Create,Read,Update,Delete) operations on a tree table with ADF Business Components.

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS2 + HR Schema]

The rest of the post is targeting mainly beginners who started using af:treetable with ADF Business Components. The sample is built using classic Department-Employee use case. Department information is displayed as parent rows and employees belonging to a department are displayed as child rows. The rendered page look like as shown below.



I'm not discussing creation of tree table here. If you want to know about the creation part, please refer chapter23.4 Using Trees to Display Master-Detail Objects from Fusion Developer's Guide.

Adding create functionality to a tree table

A very common(frequent) mistake that I noticed while adding Create/Delete operations on a tree table is that, developer may tend to treat tree table as same as a simple table - where you have only one level of information ( in effect only one RowIterator ). Please note that, dragging and dropping CreateInsert/Delete from the DataControl palette may not help you to deal with child( second level ) nodes of tree table.
Oops...Why? This is well explained in developers guide, please go through this topic View Link Accessors Versus Data Model View Link Instances from Fusion Developer's Guide.

So If you need to create/delete child nodes/rows, you may need to access that specific RowIterator pertaining to the selected node(row) and do the manipulations over there.

Hence, our first step is to identify the selected row's Key and the RowIterator containing this selected row. The below given code written in a manged bean may help you to identify the RowIterator for a selected node/row from a tree table.

public RowIterator getSelectedNodeRowIterator() {
  if (getSampleTreeTable() != null &&
    getSampleTreeTable().getSelectedRowKeys() != null) {
    for (Object opaqueFacesKey :
     getSampleTreeTable().getSelectedRowKeys()) {
    getSampleTreeTable().setRowKey(opaqueFacesKey);
    return ((JUCtrlHierNodeBinding)getSampleTreeTable().getRowData()).getRowIterator();
    }
 }
 return null;
}

public Key getSelectedNodeRowKey() {
  if (getSampleTreeTable() != null &&
    getSampleTreeTable().getSelectedRowKeys() != null) {
    for (Object opaqueFacesKey :
     getSampleTreeTable().getSelectedRowKeys()) {
    getSampleTreeTable().setRowKey(opaqueFacesKey);
    return ((JUCtrlHierNodeBinding)getSampleTreeTable().getRowData()).getRowKey();
    }
  }
  return null;
}

Where do I need to use this Key and the RowIterator?
These parameters needs to supplied to the create method, which we will see soon.

Cool, next step is to define a method to create a child row under the selected node/row. As these operations deal with BC components(EOs/VOs), it's always good to keep this logic inside the BC layer. So ApplicationModule seems to be the better candidate here. Expose a create method from ApplicationModule that takes the above said parameters. Time to explode the create method, this guy need to identify the selected row from RowIterator using the Key. The below piece of code does that.

Row[] found = ri.findByKey(selectedNodeKey, 1); //selected row

Alright...We got the selected row, job pending is to create the child rows below the selected one. This is an easy task, the ViewLink Accessor Attribute may help you to get hold of child rows from a parent row(selected row). ViewLink Accessor Attribute gives the ability to access the object at other end of the view link.

Where can I find the Accessor Attribute name?

I assume that, you have already created EnittyObjects, ViewObjects and necessary Associations and ViewLinks. Accessor Attribute name can be found from the ViewLink that you define between source and a target view object(parent and child). For example, consider the ViewLink defined between Department and Employee ViewObjects, shown below. The destination(EmployeesView) can be accessed from the source(DepartmentsView) by using the accessor attribute 'Employees'.


Alright...enough theory, let us switch back to the the 'create' method that we were discussing. Our basic infrastructure is in place, the remaining step is create the row and fill in the necessary attribute values. As the sample that we discuss here deals with two different types of rows, different treatment needs to be given for both parent and child row creations. This can be done by checking the row type as shown below.
if (nodeDefname.equals(deptViewDefName))....

Now the create method in the ApplicationModule may look like as shown below.

public void createChildren(RowIterator ri, Key selectedNodeKey) {
 final String deptViewDefName = "model.DepartmentsView";
 final String empViewDefName = "model.EmployeesView";
 if (ri != null && selectedNodeKey != null) {

    Row last = ri.last();
    Key lastRowKey = last.getKey();
    // if the select row is not the last row in the row iterator...

    Row[] found = ri.findByKey(selectedNodeKey, 1);
    if (found != null && found.length == 1) {
    Row foundRow = found[0];
    String nodeDefname =
        foundRow.getStructureDef().getDefFullName();

    if (nodeDefname.equals(deptViewDefName)) {
        RowSet parents =
        (RowSet)foundRow.getAttribute("EmployeesView");
        Row childrow = parents.createRow();
        parents.insertRow(childrow);
    } else {
        RowSet parents =
        (RowSet)foundRow.getAttribute("EmployeesView");
        Row childrow = parents.createRow();
        childrow.setAttribute("DepartmentId",
                 foundRow.getAttribute("DepartmentId"));
       parents.insertRow(childrow);
    }

    } else {
    System.out.println("Node not Found for " + selectedNodeKey);
    }

  }
}


You may need to bind the the above 'create' method with a button on your tree table page. The binding defined for the above method in my sample looks like as shown below.



A glance at Run-time Coordination

Below diagram illustrates how the View, Binding and BC Layers coordinates to render the tree table.

Adding delete functionality to a tree table

The method signature remains same as the one that we defined for implementing create functionality. So this part does not need further explanation. I'm copying the code below.

public void deleteChildren(RowIterator ri, Key selectedNodeKey) {
  if (ri != null && selectedNodeKey != null) {

    Row[] rows = ri.findByKey(selectedNodeKey, 1);
    if (rows != null) {

    for (Row row : rows) {
        row.remove();
    }

    } else {
     System.out.println("Node not Found for " + selectedNodeKey);
    }

  }
}

Further Readings..

The below blog posts(from Andrejus, Frank and Steve) may help you to understand the various use cases on a tree table.

Tuesday, May 4, 2010

Debugging "java.lang.IllegalStateException: Duplicate component id" error

I recently noticed an issue reported with below stack trace. Here the run time complaints about the presence of duplicate id in the jsf page. Interestingly, none of the components used in the page shares the same id. So what goes wrong here?

java.lang.IllegalStateException: Duplicate component id: 'pt1:USma:0:MAnt1:0:pt1:Perio1:0:ap1:r9:requestBtns:localeView:si1', first used in tag: 'com.sun.faces.taglib.jsf_core.SelectItemsTag'
+id: j_id__ctru0
  type: javax.faces.component.UIViewRoot@53eff13
   +id: doc1
    type: RichDocumentUIXFacesBeanImpl, id=doc1
     +id: pt1
      type: RichPageTemplateoracle.adf.view.rich.component.fragment.UIXInclude$ContextualFacesBeanWrapper@4bcc1b1, id=pt1
       +id: r1
        type: RichRegionUIXFacesBeanImpl, id=r1
         +id: p1
          type: RichPopupUIXFacesBeanImpl, id=p1
           +id: dia1
          

Let me share a couple of points which may help you to narrow down this issue.

1. If you use multiple instances of same task flow in a page, please make sure that you define separate binding for each task flow. This is really needed as each task flow instance needs to live in their own world.

Wrong Implementation:
<af:region value="#{bindings.myTaskflowdefinition1.regionModel}"
                   id="r1"/>
<af:region value="#{bindings.myTaskflowdefinition1.regionModel}"
                   id="r2"/>
Correct Implementation:
<af:region value="#{bindings.myTaskflowdefinition1.regionModel}"
                   id="r1"/>
<af:region value="#{bindings.myTaskflowdefinition2.regionModel}"
                   id="r2"/>
2. While building a task flow, please make sure that scope specified for manged bean is not visible from another instances of the same task flow. This means that backingBean, view and pageFlow may be the right 'scope' choices depending on your use case requirement. Please avoid request, session and application scopes unless you have specific reason for that.
Why? The issue is that, in some cases you may need to access the UI component from manged bean and apparently you may go for binding the component with bean. Here, if you chose a scope which is visible from another instances of the same task flow, that may cause unpredictable behavior later and may result in the above said error as well.

Please go through chapter 14.2.4 What You May Need to Know About Memory Scope for Task Flows from Oracle® Fusion Middleware Fusion Developer's Guide to learn more.