Saturday, October 23, 2010

What you may need to know about <events> definitions in PageDef file

I'm not sure how many of you have noticed the 'generated' PageDef file entries when you define a Contextual Event using the editor(PS2 release). If you define Contextual Event for a command button, the generated PageDef entries may look like as shown below
<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
                version="11.1.1.56.60" id="untitled8PageDef"
                Package="view.pageDefs">
  <parameters/>
  <executables>
    <variableIterator id="variables"/>
  </executables>
  <bindings>
    <eventBinding id="eventBinding"
                  Listener="javax.faces.event.ActionListener">
      <events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
        <event name="SampleEvent" customPayLoad="Test"/>
      </events>
    </eventBinding>
  </bindings>
  <events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
    <event name="SampleEvent" customPayLoad="Test"/>
  </events>
</pageDefinition>

You may notice that <events> repeats two times - one under <eventBinding> and another at root level, right inside <pageDefinition>. Please note that, Contextual Event Runtime is interested only in the one which appears inside <eventBinding>. The <events> right inside <pageDefinition> is supposed to be used by design time - while defining Contextual Event using existing events. It acts more like a template. (PS2 release doesn't seems to have this feature, hopefully upcoming release of JDeveloper may have this feature enabled)

Friday, October 15, 2010

Preventing user input on a fusion web page when the server is busy

Recently I noticed a reply from Gary Van Matre (Oracle) in an internal discussion thread on 'blocking user input' on a data capture form using JavaScript when the server is busy serving previous request. btw, this reminded me the Glasspane in Java Swing applications - good old days:)

Solution looks very simple and elegant, you may need to just call event.preventUserInput(); from a JavaScript method.

API doc says:
Calling this method prevents all user input while the event is being processed, including while the event is propagated to the server. The UI is automatically unblocked when processing for this event is complete. Ignores request for events that do not propagate to the server.
<af:resource type="JavaScript">
 function showGlasspane(event){
    event.preventUserInput(); 
  }
 </af:resource>
<af:commandButton actionListener="#{bindings.ExecuteWithParams.execute}"
      text="Search" clientComponent="true"
      id="cb1" partialTriggers="t1">
    <af:clientListener method="showGlasspane" type="action"/>
</af:commandButton>


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

How to run this sample?

1. Run test.jspx
2. Click on Search button.

This action in turn trigger the client side JavaScript method first, and then calls ExecuteWithParams action on server. You may notice the change in the mouse cursor at this stage - mouse cursor to an hourglass. And changes back to normal mode, when processing for this event is complete. Thanks to Gary and AdfBaseEvent.preventUserInput()!

How to set Bind Variable Values at runtime ?

In this post I'm sharing a couple of approaches for programmatically setting bind variables values at run time. This post is an attempt to explain 'When to use what ?'[ In case if you are familiar with 'Bind Variables' in ADF BC, please refer Section 5.10, Working with Bind Variables in Fusion Developer's Guide ]

1. Set the Bind Variable value using RowSet::setNamedWhereClauseParam(...)

You can use use the setNamedWhereClauseParam(...) method on the ViewObject interface (which extends oracle.jbo.RowSet) to set the value for bind variables. Please note this sets the value on default RowSet. In other words, this doesn't have any effect on the secondary RowSets that you/system generates.
ViewObject vo = am.findViewObject("EmployeesView1");
vo.setNamedWhereClauseParam("bindVarDeptId", new Number(10));
vo.executeQuery();

2. Set the Bind Variable value using ViewObject's VariableValueManager::setVariableValue(...)

VariableValueManager Manages named variables and their values. The bind variable value set by calling VariableValueManager::setVariableValue(...) may reflect across all RowSets if these RowSets have not set the bind variable values by calling RowSet::setNamedWhereClauseParam(...). Let me try to clarify this part. Please note that ADF BC enables each Rowset to have its own copy of bind variables to produce and process multiple row sets based on different combination of bind variable values. If you chose to call yourRowSet.setNamedWhereClauseParam(...), then this takes precedence over VariableValueManager's copy from the parent/owner ViewObject.


What you may need to know about setting values for Bind Variables when used inside a ViewCriteria?


You can assign the bind variable to a view criteria item. In this case, the bind variables allow you to change the values for attributes you will use to filter the view object row set. Please note that the Search Form built using ViewCriteria gets and sets bind variable values from the VariableValueManager of the ViewObject. This means that, if you need to initialize the bind variable values when used inside a ViewCriteria, please call VariableValueManager::setVariableValue(...). The ViewObject.setNamedWhereClauseParam(...) will not help you here.
ViewObject vo = am.findViewObject("EmployeesView1");
VariableValueManager vm = vo.ensureVariableManager();
vm.setVariableValue("bindVarDeptId", deptId);


Learn More ...


There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Thursday, October 14, 2010

Defining multiple eventBindings

While working on specific use case, came across scenario where I had to define separate Contextual Events for different button's on a page.

Story in nutshell, a page is having two command buttons, each one should trigger Contextual Event with different payloads. Unfortunately 'Contextual Event' editor was not letting me to generate two event bindings.[Please note that this issue is rectified in latest internal builds, hopefully next public release may have this fix] So I went ahead and created this binding manually. I'm just sharing the manual binding tat I created here. I hope this may help you if you face similar issues during development.

JSF tags may look like as shown below

<af:commandButton text="commandButton 1" id="cb1"
  actionListener="#{bindings.eventBinding1.listener.processAction}"/>
<af:commandButton text="commandButton 2" id="cb2"
  actionListener="#{bindings.eventBinding2.listener.processAction}"/>

Page definition file may look like as shown below

  <bindings>
    <eventBinding id="eventBinding1"
                  Listener="javax.faces.event.ActionListener">
      <events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
        <event name="Event1" customPayLoad="Test1" eventType="Action Event"/>
      </events>
    </eventBinding>
    <eventBinding id="eventBinding2"
                  Listener="javax.faces.event.ActionListener">
      <events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
        <event name="Event2" customPayLoad="Test2" eventType="Action Event"/>
      </events>
    </eventBinding>
  </bindings>

Tuesday, October 12, 2010

Creating View Criteria having Bind Variables at run time

ADF BC let you to define View Criteria on the fly. In this post, I'm sharing some tips to create and populate criteria items with bind variables to implement the 'query-by-example' at run time.

The following code snippet illustrates the APIs to be used for creating View Criteria with Bind Variables.

String   attribName = "FirstName";
ViewObjectImpl voEmp = getEmployeesView1();


VariableValueManager vvm = voEmp.ensureVariableManager();

ViewCriteria vc = voEmp.createViewCriteria();
ViewCriteriaRow vcr = vc.createViewCriteriaRow();
ViewCriteriaItem vci = vcr.ensureCriteriaItem(attribName);
vci.setOperator(JboCompOper.OPER_LIKE);
vci.setRequired(ViewCriteriaItem.VCITEM_REQUIRED);

VariableImpl fstNameVar =
    (VariableImpl)vvm.addVariable("dynamicBindVarAttribute");
fstNameVar.setJavaType(String.class);
fstNameVar.setMandatory(true);
fstNameVar.setUpdateableFlag(Variable.UPDATEABLE);
fstNameVar.setVariableKind(Variable.VAR_KIND_VIEW_CRITERIA_PARAM);
fstNameVar.setProperty(AttributeHints.ATTRIBUTE_DISPLAY_HINT,
               AttributeHints.ATTRIBUTE_DISPLAY_HINT_HIDE);


vci.setValue(0, ":dynamicBindVarAttribute");
vci.setIsBindVarValue(0, true);
vvm.setVariableValue(fstNameVar, "A%");

vc.insertRow(vcr);
voEmp.applyViewCriteria(vc);
voEmp.executeQuery();


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

A glance at the implementation.

This example contains a task flow 'dynamicVC-sample-tf-definition' with a default method activity which is wired to AppModuleImpl::demoDynamicVCWithBindVar(String attribName). This method (demoDynamicVCWithBindVar) creates ViewCriteria on the fly, based on the input parameter passed from the callee. Please take a look at the source ( relevant part is copied above for your reference ).


Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Tuesday, October 5, 2010

A public API to get the value originally read for an Entity Attribute from the database

There is an 'undocumented and smart' API to get get the value originally read for an Entity Attribute from the database. Thanks to Steve for sharing this. You may need to call EntityImpl::getAttribute(attrIndex,EntityImpl.ORIGINAL_VERSION) to get the original value of the attribute.

As you may be aware, there is a 'well known' protected method EntityImpl::getPostedAttribute(int index) also available for the same purpose. In this case, you may need to sub class the EntityImpl
to increase the visibility, if you want to use it in your business service implementation.

Enabling Validation for Table Filters

You can enable filtering for UI Tables without much effort while building the web pages using ADF Faces. Please refer Enabling Filtering in Tables in Web User Interface Developer's Guide to know more about this topic.

By design, the input validators are turned off to allow for entering characters for operators such as > and < to modify the search criteria. However, you are free to override the default filter fields by using the filter facet for the <af:column>. You can provide your own components for filter fields using the filter facet, and add converter/validator of your choice to these fields.

Please see the below jspx snippet. Run-time renders the components specified inside the 'filter' facet for the filter displayed on top of EmployeeId column.
  <af:table value="#{bindings.EmployeesView1.collectionModel}" var="row"
                  rows="#{bindings.EmployeesView1.rangeSize}"
                  fetchSize="#{bindings.EmployeesView1.rangeSize}"
                  rowBandingInterval="0"
                  filterModel="#{bindings.EmployeesView1Query.queryDescriptor}"
                  queryListener="#{bindings.EmployeesView1Query.processQuery}"
                  filterVisible="true" varStatus="vs" id="t1">
  <af:column sortProperty="EmployeeId" filterable="true"
         sortable="false"
         headerText="#{bindings.EmployeesView1.hints.EmployeeId.label}"
         id="c5">
    <af:inputText value="#{row.bindings.EmployeeId.inputValue}"
          label="#{bindings.EmployeesView1.hints.EmployeeId.label}"
          required="#{bindings.EmployeesView1.hints.EmployeeId.mandatory}"
          columns="#{bindings.EmployeesView1.hints.EmployeeId.displayWidth}"
          maximumLength="#{bindings.EmployeesView1.hints.EmployeeId.precision}"
          shortDesc="#{bindings.EmployeesView1.hints.EmployeeId.tooltip}"
          id="it8">
      <f:validator binding="#{row.bindings.EmployeeId.validator}"/>
      <af:convertNumber groupingUsed="false"
            pattern="#{bindings.EmployeesView1.hints.EmployeeId.format}"/>
    </af:inputText>
   <f:facet name="filter">
     <af:inputText value="#{vs.filterCriteria.EmployeeId}" id="id31"
               label=" ">
       <af:convertNumber groupingUsed="false"
                 pattern="#{bindings.EmployeesView1.hints.EmployeeId.format}"/>
     </af:inputText>
    </f:facet>
  </af:column> 
          ....
  </af:table>