Wednesday, June 24, 2009

to refresh Lov when its launching

RichInputListOfValues lovComp = (RichInputListOfValues)launchPopupEvent.getComponent(); FacesCtrlLOVBinding.ListOfValuesModelImpl lovModel = null; lovModel = (FacesCtrlLOVBinding.ListOfValuesModelImpl) lovComp.getModel(); lovModel.performQuery(lovModel.getQueryDescriptor());

Friday, June 19, 2009

Implementing auto suggest functionality in ADF Faces Rich Client

Introduction

A native auto suggest component for ADF Faces Rich Client is on the list of new features planned for a future release of Oracle JDeveloper. However, as mentioned before, its less the component than the functionality that is of interest and the functionality is what can be build with ADF Faces Rich client on-board means today. This articles explains the architecture and the JDeveloper 11g workspace of a first prototype that we plan to enhance to a declarative component to a later point in time. We decided to release our prototype with this article to make the suggest functionality available to ADF Faces customers in a guide for manual implementation.

Architecture

The auto suggest functionality requires a client side and a server side implementation that communicate with each other using an asynchronous communication channel. On the server side we use logic to provide the list of values and to filter the list with each keystroke of the user. The list of values is chosen to display strings as labels and values. On the client side, of course, JavaScript is used to listen for the user keyboard input, to display the suggest list and to send the user entered characters as a payload to the server to filter the data.

A Poor Man's Process Chart

In this version of auto suggest, an af:inputTextField is used as the data input component. As soon as the user starts typing into the field, a JavaScript event is captured by the af:clientListener component, which passes the event on to a JavaScript function to handle the request. The JavaScript function looks at the incoming key code and if it is a valid character, opens the popup dialog below the text field if it is not open. The list box queries the list data from a managed bean that we added as an abstraction layer between the view and the model to allow list data to be read from ADF, POJO and similar. The truth also is that we need this abstraction layer to keep the suggest functionality generic, because we want the developer to decide about the caching strategy for fetched data. In the sample we use ADF Business Components to query the data and here we have the option to tell it to perform follow up queries from memory instead of a database query. When the list data is refreshed, the list component is added as a partial target to refresh at the end of the request. The input focus is put back to the input text field for the user to give us the next character.


ADF Business Components

The suggest list is retrieved from a client method that is exposed on the ADF Business Components Application Module. The method takes a string as an input argument to query the Jobs View Object and returns a List of strings back to the client.
Managed Bean

Two managed beans are used in this prototype: SuggestList and DemoAdfBcSuggestModel. The DemoAdfBcSuggestModel abstracts the ADF Business Components query and returns the list of strings to the SuggestList bean to provide the result to the client. If you plan to customize this sample to your needs, then you need to replace the DemoAdfBcSuggestModel bean with your implementation. The SuggestList provides two key methods:

public ArrayList<SelectItem> getJobList()

This method populates the suggest list. It is referenced from the f:selectItems element, which is a child element of the af:selectOneListbox element that resides in an af:popup component to suggest the values to the user.

 public ArrayList<SelectItem> getJobList() {
//first time query is against database
if (jobList == null){
jobList = new ArrayList<SelectItem>();
List<String> filteredList = null;
filteredList = suggestProvider.filteredValues(srchString, false);
jobList = populateList(filteredList);
}
return jobList;
}


private ArrayList<SelectItem> populateList(List<String> filteredList) {
ArrayList<SelectItem> _list = new ArrayList<SelectItem>();
for(String suggestString : filteredList){
SelectItem si = new SelectItem();
si.setValue(suggestString);
si.setLabel(suggestString);
_list.add(si);
}
return _list;
}

public void doFilterList
On every user keystroke in the suggest input field, the browser client calls the doFilterList method through the af:serverListener component.
 public void doFilterList(ClientEvent clientEvent) {
// set the content for the suggest list
srchString = (String)clientEvent.getParameters().get("filterString");
List<String> filteredList = suggestProvider.filteredValues(srchString, true);
jobList = populateList(filteredList);
RequestContext.getCurrentInstance().addPartialTarget(suggestList);
}

The af:selectOneListbox is refreshed at the end of each call to the doFilterList method so the new list value is populated

JSF page markup


The suggest field has two af:clientListener components assigned that listen for the keyUp and keyDown event. All keyboard events on the suggest field are send to the handleKeyUpOnSuggestField JavaScript function, which takes the event object as an input argument. The event object provides information about the event source, the component receiving the key stroke, and the keyCode pressed. Using the ADF Faces RC client JavaScript APIs, we don't need to deal with browser differences, which is big relief.


<!-- START Suggest Field -->
<af:inputText id="suggestField"
clientComponent="true"
value="#{bindings.JobId.inputValue}"
label="#{bindings.JobId.hints.label}"
required="#{bindings.JobId.hints.mandatory}"
columns="#{bindings.JobId.hints.displayWidth}"
maximumLength="#{bindings.JobId.hints.precision}"
shortDesc="#{bindings.JobId.hints.tooltip}">
<f:validator binding="#{bindings.JobId.validator}"/>
<af:clientListener method="handleKeyUpOnSuggestField"
type="keyUp"/>
<af:clientListener method="handleKeyDownOnSuggestField"
type="keyDown"/>

</af:inputText>
<!-- END Suggest Field -->

The suggest list is defined as follows

<!-- START Suggest Popup -->
<af:popup id="suggestPopup" contentDelivery="immediate" animate="false" clientComponent="true">
<af:selectOneListbox id="joblist" contentStyle="width: 250px;"
size="15" valuePassThru="true"
binding="#{SuggestListBean.suggestList}">
<f:selectItems value="#{SuggestListBean.jobList}"/>
<af:clientListener method="handleListSelection" type="keyUp"/>
<af:clientListener method="handleListSelection" type="click"/>

</af:selectOneListbox>
<af:serverListener type="suggestServerListener"
method="#{SuggestListBean.doFilterList}"/>

</af:popup>
<!-- END Suggest Popup -->

The af:selectOneListbox has two af:clientListener defined that listen for the keyUp and click event for the user to select a value (Enter key , click) or to cancel the action (Esc). The af:serverListener is called from the JavaScript that is executed when the user enters a new character into the suggest input field. The af:serverListener calls the doFilterList method in the SuggestList bean.


JavaScript


JavaScript has become popular with Ajax and also is an option to use with ADF Faces Rich Client. Unlike previous versions of ADF Faces, ADF Faces Rich Client has a real client side framework that by large is used internally, but also exposes public functions to prevent users from hacking the DOM tree.



Start Disclaimer



Before we get into JavaScript programming, we should mention that using JavaScript in ADF Faces Rich Client is the second best solution. The best solution always is to use the JavaServer Faces programming APIs because this is what we have good chances to see migrated from one release to the other, which also includes new browser versions that today we don't know are coming. However, some usecases, like the suggest functionality, require JavaScript to put the logic where it is needed and to save network roundtrips where possible.


End Disclaimer




In this version of Oracle JDeveloper 11g, JavaScript is added to the metacontainer facet of the af:document element. As soon as we see JDeveloper 11g R1 being productive, this part of the code may be changed to the new af:resource element.


<f:facet name="metaContainer">
<af:group>
<![CDATA[
<script>
//******************************************************************
//PROTOTYPE: AUTO SUGGEST
//STATUS: 1.0
//authors: Frank Nimphius, Maroun Imad
//Functionality
//====================================
// Implemented in v 1.0
//1 - Suggest window opens JOB_ID field
//2 - Initial query is to database, follow up queries are in memory
//3 - Enter list with down arrow
//4 - Enter key and mouse click to select
//5 - ESC to close
//******************************************************************

function handleKeyUpOnSuggestField(evt){
// start the popup aligned to the component that launched it
suggestPopup = evt.getSource().findComponent("suggestPopup");
inputField = evt.getSource();

//don't open when user "tabs" into field
if (suggestPopup.isShowing() == false &&
evt.getKeyCode()!= AdfKeyStroke.TAB_KEY){
//We reference a DOM area by using getClientId()+"::content" to position the suggest list. Shame
//on use that we are doing this, but there is no other option we found. Keep in mind that using
//direct DOM references may break the code in the future if the rendering of the component changes
//for whatever reason. For now, we want to feel safe and continue using it

hints = {align:AdfRichPopup.ALIGN_AFTER_START, alignId:evt.getSource().getClientId()+"::content"};
suggestPopup.show(hints);
//disable popup hide to avoid popup to flicker on key press. Note that we override the default framework
//behavior of the popup component for this instance only. When hiding the popup, we re-implement the default
//functionality. By all means, never change the framework functionality on the prototype level as this could
//have an unpredictable impact on all nstances of this type

suggestPopup.hide = function(){}
}

//suppress server access for the following keys
//for better performance

if (evt.getKeyCode() == AdfKeyStroke.ARROWLEFT_KEY
evt.getKeyCode() == AdfKeyStroke.ARROWRIGHT_KEY
evt.getKeyCode() == AdfKeyStroke.ARROWDOWN_KEY
evt.getKeyCode() == AdfKeyStroke.SHIFT_MASK
evt.getKeyCode() == AdfKeyStroke.END_KEY
evt.getKeyCode() == AdfKeyStroke.ALT_KEY
evt.getKeyCode() == AdfKeyStroke.HOME_KEY) {
return false;
}
//close the popup when teh ESC key is pressed
if (evt.getKeyCode() == AdfKeyStroke.ESC_KEY){
hidePopup(evt);
return false;
}

// get the user typed values
valueStr = inputField.getSubmittedValue();
// query suggest list on the server. The custom event references the af:serverListener component
// which calls a server side managed bean method. The payload we send is the current string entered
// in the suggest field

AdfCustomEvent.queue(suggestPopup,"suggestServerListener",
// Send single parameter
{filterString:valueStr},true);

// put focus back to the input text field. We set the timout to 400 ms, which is not the final answer but a
// value that worked for us. In a next version, we may improve this

setTimeout("inputField.focus();",400);
}

//TAB and ARROW DOWN keys navigate to the suggest popup we need to handle this in the key down event as otherwise
//the TAB doesn't work. Note that a TAB key cannot be used to navigate in the suggest list. Use the arrow keys for
//this. The TAB key can be done to navigate the list, but this requires some more JavaScript that we didn't complete
//in time

function handleKeyDownOnSuggestField(evt){
if (evt.getKeyCode() == AdfKeyStroke.ARROWDOWN_KEY) {
selectList = evt.getSource().findComponent("joblist");
selectList.focus();
return false;
}
else{
return false;
}
}

//method called when pressing a key or a mouse button on the list. The ENTER key or a mosue click select
//the list value and write it back to the input text field (the suggest field)

function handleListSelection(evt){
if(evt.getKeyCode() == AdfKeyStroke.ENTER_KEY
evt.getType() == AdfUIInputEvent.CLICK_EVENT_TYPE){
var list = evt.getSource();
evt.cancel();
var listValue = list.getProperty("value");
hidePopup(evt);
inputField = evt.getSource().findComponent("suggestField");
inputField.setValue(listValue);
}
//cancel dialog
else if (evt.getKeyCode() == AdfKeyStroke.ESC_KEY){
hidePopup(evt);
}
}

//function that re-implements the node functionality for the popup to then call it
//Please do as if you've never seen this code ;-)

function hidePopup(evt){
var suggestPopup = evt.getSource().findComponent("suggestPopup");
//re-implement close functionality
suggestPopup.hide = AdfRichPopup.prototype.hide;
suggestPopup.hide();
}
</script>
]]>
</af:group>
</f:facet>

Detecting and handling user session expiry

A frequent question on the JDeveloper OTN forum, and also one that has been asked by customers directly, is how to detect and graceful handle user session expiry due to user inactivity. The problem of user inactivity is that there is no way in JavaEE for the server to call the client when the session has expired. Though you could use JavaScript on the client display to count down the session timeout, eventually showing an alert or redirecting the browser, this goes with a lot of overhead. The main concern raised against unhandled session invalidation due to user inactivity is that the next user request leads to unpredictable results and errors messages. Because all information stored in the user session get lost upon session expiry, you can't recover the session and need to start over again.

The solution to this problem is a servlet filter that works on top of the Faces servlet. The web.xml file would have the servlet configured as follows


<filter>
<filter-name>ApplicationSessionExpiryFilter</filter-name>
<filter-class>adf.sample.ApplicationSessionExpiryFilter</filter-class>
<init-param>
<param-name>SessionTimeoutRedirect</param-name>
<param-value>SessionHasExpired.jspx</param-value>
</init-param>
</filter>

This configures the "ApplicationSessionExpiryFilter" servlet with an initialization parameter for the administrator to configure the page that the filter redirects the request to. In this example, the page is a simple JSP page that only prints a message so the user knows what has happened.

Further in the web.xml file, the filter is assigned to the JavaServer Faces servlet as follows


<filter-mapping>
<filter-name>ApplicationSessionExpiryFilter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>

The Servlet filter code compares the session Id of the request with the current session Id. This nicely handles the issue of the JavaEE container implicitly creating a new user session for the incoming request. The only special case to be handled is where the incoming request doesn't have an associated session ID. This is the case for the initial application request.


package adf.sample;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ApplicationSessionExpiryFilter implements Filter {
private FilterConfig _filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
_filterConfig = filterConfig;
}
public void destroy() {
_filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String requestedSession = ((HttpServletRequest)request).getRequestedSessionId();
String currentWebSession = ((HttpServletRequest)request).getSession().getId();
boolean sessionOk = currentWebSession.equalsIgnoreCase(requestedSession);
// if the requested session is null then this is the first application
// request and "false" is acceptable
if (!sessionOk && requestedSession != null){
// the session has expired or renewed. Redirect request
((HttpServletResponse) response).sendRedirect(_filterConfig.getInitParameter("SessionTimeoutRedirect")); }
else{
chain.doFilter(request, response);
}
}
}

This servlet filter works pretty well, except for sessions that are expired because of active session invalidation e.g. when nuking the session to log out of container managed authentication. In this case my recommendation is to extend line 39 to also include a check if security is required. This can be through another initialization parameter that holds the name of a page that the request is redirected to upon logout. In this case you don't redirect the request to the error page but continue with a newly created session.Ps.: For testing and development, set the following parameter in web.xml to 1 so you don't have to wait 35 minutes


<session-config>
<session-timeout>1</session-timeout>
</session-config>


Thursday, June 18, 2009

How to implement drop down list in ADF

In this how-to example I am defining a department drop down list, which shows a list of departments.

Add the following code in the ADF page.

          <af:selectOneChoice label="Department"
value="#{ModelData.department}">
<f:selectItems value="#{DepartmentOptions.departments}"/>
</af:selectOneChoice>

In ADF, selectOneChoice is used for drop down list.

selectOneChoice picks the list of values from 'selectItems' tag.

You can define a managed bean to bind an ArrayList of SelectItem objects to 'selectItems'. or can define a view object and bindings. In this example I have defined a managed bean which provides an ArrayList of SelectItem objects.

Department list Backing bean code.

import java.util.ArrayList;
import java.util.List;

import javax.faces.model.SelectItem;

public class DepartmentOptions {
private List departments;

public DepartmentOptions() {
departments = new ArrayList();
SelectItem department = new SelectItem("10", "Electric");
departments.add(department);
department = new SelectItem("20", "Mechanic");
departments.add(department);
department = new SelectItem("30", "Computer");
departments.add(department);
}

public void setDepartments(List departments) {
this.departments = departments;
}

public List getDepartments() {
return departments;
}
}

How to Implement Dependent Drop Down List in ADF

In this How-to example, I am going to explain, how dependent drop down list can be implemented in ADF.

Here I am creating two drop down lists "Department" and "Employee".
On selecting department, the list of employees will change.


I am assuming that you have sample HR schema installed in your database.

Create View Object DepartmentListVO for Department SelectItems.
Query: Select department_id, department_name from departments

Create View Object EmployeeListVO for Employee SelectItems.
Query: select employee_id, first_name from employees where dapartment_id = :DeptId

Define Bind variables DeptId in EmployeeListVO

Create View Object EmpDeptVO for defining the form elements.
This query varies according to your requirement. At least include employee_id and department_id in your select clause.

Define an Application Module and add above view objects to this application module.

Create a jspx page, drag EmpDeptVO to the jspx page and drop EmpDeptVO as ADF form.

Delete form elements for employee_id and department_id from the page.

Drag DepartmentId from data control under EmpDeptVO and drop as select one choice.
Click on add to define binding for List. Select DepartmentListVO from the Add data source drop down.
Select Display Attribute as DepartmentName.

Id of this select one chioce is auto populated as selectOneChoice1
Drag EmployeeId from data control under EmpDeptVO and drop as select one choice.
Click on add to define binding for List. Select EmployeeListVO from the Add data source drop down.
Select Display Attribute as FirstName.

Id of this select one chioce is auto populated as selectOneChoice2

Select selectOneChoice1 in structure window and go to properties.
Set AutoSubmit property to true

Select selectOneChoice2 in structure window and go to properties.
Set PartialTruggers property to selectOneChoice1

          <af:selectOneChoice value="#{bindings.Departmentid.inputValue}"
label="Department"
id="selectOneChoice1" autoSubmit="true"">
<f:selectItems value="#{bindings.Departmentid.items}"
id="selectItems1"/>
</af:selectOneChoice>
<af:selectOneChoice value="#{bindings.Employeeid.inputValue}"
label="Employee"
id="selectOneChoice2"
partialTriggers="selectOneChoice1">
<f:selectItems value="#{bindings.Employeeid.items}"
id="selectItems2"/>
</af:selectOneChoice>


Right click on the page and go to page definition.
Right click on Bindings -Select 'Insert Inside Bindings' - Select 'Generic Bindings' - select 'Action'

Select EmployeeListVO from data collection
Select Iterator as EmployeeListVOIterator
Select Operation as ExecuteWithParams
Set the value of parameter 'DeptId' as #{bindings.Departmentid.inputValue}
Click OK

Right click on executables - Select 'Insert Inside executables' - select 'InvokeAction'

Give one meaningful id 'InvokeExecuteWithParams'.
Set 'Binds' as 'ExecuteWithParams'
Set 'Refresh' as 'RenderModel'
Click Finish

Drag InvokeExecuteWithParams above EmployeeListVOIterator

Run the page.

  <bindings>
<action IterBinding="EmployeeListVOIterator" id="ExecuteWithParams"
InstanceName="HRAppModuleDataControl.EmployeeListVO"
DataControl="HRAppModuleDataControl" RequiresUpdateModel="true"
Action="executeWithParams">
<NamedData NDName="DeptId"
NDValue="#{bindings.Departmentid.inputValue}"
NDType="oracle.jbo.domain.Number"/>
</action>
..........
..........
</bindings>

<executables>
<invokeAction id="InvokeExecuteWithParams" Binds="ExecuteWithParams"
Refresh="renderModel"/>
<iterator Binds="EmployeeListVOIterator" RangeSize="-1"
DataControl="HRAppModuleDataControl" id="EmployeeListVOIterator"/>
...........
...........
</executables>



How to get Reference of one Managed Bean from other Managed Bean

FacesContext ctx = FacesContext.getCurrentInstance();

Application app = ctx.getApplication();

MyManagedBeanClass mb = (MyManagedBeanClass) app.getVariableResolver().resolveVariable(ctx,"MyManagedBean");

How to override the sort behavior of table column ?

Add following code to managed bean that is accessed from the table's sort listener. In this example, whenever users try to sort on the DepartmentName by clicking onto the table header, the sort criteria is modified to DepartmentId .

import java.util.ArrayList;
import java.util.List;

import oracle.adf.view.faces.component.core.data.CoreTable;
import oracle.adf.view.faces.event.SortEvent;
import oracle.adf.view.faces.model.SortCriterion;


public class Sortbean {
public Sortbean() {
}

public void onSort(SortEvent sortEvent) {

List sortList = sortEvent.getSortCriteria();
SortCriterion sc = (SortCriterion) sortList.get(0);

//override sort by DepartmentName and make it sort by DepartmentId instead
if (((String)sc.getProperty()).equalsIgnoreCase("DepartmentName")){
System.out.println("You wanted to sort " +sc.getProperty());

sortList = new ArrayList();
SortCriterion sc2 = new SortCriterion("DepartmentId",true);
System.out.println("This is what I want you to sort for "+sc2.getProperty());
sortList.add(sc2);

CoreTable ct = (CoreTable)sortEvent.getComponent();
ct.setSortCriteria(sortList);
}
}
}

How to avoid JBO-35007 error?

When row currency validation fails JBO-35007 error is reaised.

Row currency validation safe gaurds ADF application from browser back button issue.

What is browser back button issue?
Form Duncan Mills ""Oracle JDeveloper 10g for Forms and PL/SQL Developers" book :
Users may be accustomed to clicking the browser's Back button to return to the preceding page or the Refresh button to reload the page. This can cause problems with web applications that use a Controller layer, like the JSF controller or Struts, because the browser Back button returns to the preceding page in the browser's page history without calling code in the Controller layer. The controller will therefore, not know the state of the application's data and the preceding page will either not load properly or may place the application into an inconsistent or unstable state. The same problem occurs with the browser's Refresh button, which just reloads the same page, again without calling Controller layer code. This is a problem in all web applications, not only those using J2EE technology or ADF libraries.

How to ignore row currnecy validation?
There are two ways to avoid this validation.(Note: As Row currency validation safe gaurds ADF application from browser back button issue, it is not suggested to ignore row currency validation)

Method I
Set the EnableTokenValidation to false in the page definition file.
1)In the Application Navigator, right-click the JSP page for which you want to disable validation, and choose Go to Pa ge Definition.
2)In the Structure window, right-click the pagenamePageDef node and choose Properties from the context menu.
3)In the PageDef Properties dialog, select the Advanced Properties tab.
4)In the Enable Token Validation box and choose False. The EnableTokenValidation attribute is added to the PageDef.xml file namespace with a value of false.

Method II
Disable row currency validation on an iterator by setting the "StateValidation" property of an iterator to "false" without disabling it for all iterators in the page definition.

How to prevent navigation thorugh browser back button using java script in ADF page

Add the following java script to Onload attribute of afh:body tag in ADF page.

if (history.forward() != null) history.forward()

Preventing execution of queries when page loads for first time (ADF)

Sometimes you want to prevent the automatic execution of a query when page loads for first time.

To achieve this ...

In ADF 10.1.3, add ${adfFacesContext.postback == true} to refresh condition of the iterator that displays the data in your page.

In ADF 11g, add #{!adfFacesContext.initialRender} to refresh condition of the iterator that displays the data in your page.