Friday May 04, 2012

Scope object serialization failed (object not serializable)

A common reason for the "SEVERE: ADFc: Scope object serialization failed (object not serializable)" error message is that application developers reference JSF components in managed beans with a scope larger than request. When using the JSF component Binding property to point to a managed bean, think backing bean and thus don't reference beans that are in scopes larger than request scope (don't: viewScope, pageFlowSope, sessionScope, applicationScope).

If, from a managed bean in a scope larger than request you need to have access to a component instance (though the other way around is a more common use case) then either

As mentioned, a more common use case is to access a bean in a scope larger than request from a backing bean. A use case for this is if you need to remember a specific state, like a list of selected values in a select many list, or the disclosure state of panel tabs.

In this case you would save the state information in a bean with a lifecycle that spans beyond request scope and access it from a request scope backing bean using a managed property or by resolving EL in Java.

Monday Apr 23, 2012

How-to invoke a method once upon application start

A requirement on the OTN forum was to execute a method only once upon application start either for the application as a whole (all user instances) or once per application user session. In addition, the method to be executed was exposed as an Operation binding on the ADF binding layer.

One way to provide a solution to this requirement is to within the combination of a phase listener on the JSPX of JSF document level and a managed bean in application or session scope (dependent on whether the method should be executed once per application start or once per application user session).

The phase listener can be configured on a JSF document, as mentioned or in the faces-config.xml file if there is no single entry to an application. For this example, we assume a single point of entry so that the phase listener can be configured on the f:view attribute.

<f:view beforePhase="#{ManagedBean.onBeforePhase}">

The event logic is configured in a bean in request scope so it could also hold component references for the page if required.

public void onBeforePhase(PhaseEvent phaseEvent) {
 //render response is called on an initial page request 
  if(phaseEvent.getPhaseId() == PhaseId.RENDER_RESPONSE){ 
    FacesContext fctx = FacesContext.getCurrentInstance(); 
    ELContext elctx = fctx.getELContext(); 
    ExpressionFactory exprFactory =
             fctx.getApplication().getExpressionFactory(); 
    //call the managed bean in application or session scope. If the 
    //bean instance already exists, then no new instance of it will be  
    //created,in which case the "initial load" method is not executed 
   
    ValueExpression ve = exprFactory.createValueExpression(
         elctx, 
         "#{your_managed_bean_in_application_or-session_scope", 
         Object.class); 
    ve.getValue(elctx); 
  }
}

The idea for this phase listener is to reference a managed bean in application scope or session scope base on your requirement. The managed bean in session or application scope invokes the method you want to invoke once per application or user session in its post construct method

//Managed Bean in application scope
import javax.annotation.PostConstruct;
... 
@PostConstruct 
public void methodInvokeOncedOnPageLoad(){  
  //access the methods you want to invoke. If they are exposed in the  
  //PageDef file, access the BindingContext --> BindingContainer --> 
  //OperationBinding. Alternatively you can call BindingContext --> 
  //findDataControl("Name as in DataBindings.cpx") --> 
  //getApplicationModule -->  findViewObject/Execute methods 
  ...
}

Note that the Java EE @PostConstruct bean is called once for each bean instantiation. In the managed bean case, the bean is instantiated once per application or session and so is the method executed once.


        
    

Monday Mar 05, 2012

Creating a custom master-detail presentation using af:panelAccordion

ADF Code Corner sample #81 explains how-to create a custom layout to render master-detail information. In the sample ADF Faces panel tabs are used to render the master data and DVT components are used in the panel tabs to show the detail information.

http://www.oracle.com/technetwork/developer-tools/adf/learnmore/81-master-detail-tab-with-graphs-394252.pdf

http://www.oracle.com/technetwork/developer-tools/adf/learnmore/81-master-detail-tab-with-graphs-394253.zip

ADF Code Corner Sample 81

The sample reads master information from the selected panel tab to then – using Java in a managed bean – query the detail record for the DVT component. While the solution documented on ADF Code Corner is efficient, there exists another one that is declarative.

Note: of course there are differences in the usage, but this I'll discuss later

The image below shows an ADF Faces panel accordion that displays the HR schema Departments table information on the accordion tabs and information about contained Employees in a panel box that shows when the accordion is expanded.

MdPresentationWithPanelAccordion

This sample uses an ADF tree binding to render the panel accordion tabs and the contained employee detail information. The iteration is done by two af:forEach tags, as shown below

<af:panelAccordion id="pa1" dimensionsFrom="auto">
 <af:forEach items="#{bindings.allDepartmentsWithEmployees.children}" var="departmentsVar">
   <af:showDetailItem text="#{departmentsVar.DepartmentName}" id="sdi1"
                      stretchChildren="first">
    <af:panelBox text="Employees" id="pb1" showDisclosure="false" type="stretch">
       <f:facet name="toolbar"/>
          <af:panelGroupLayout id="pgl1" layout="scroll">
             <af:forEach items="#{departmentsVar.children}" var="employeesVar">
                 <af:outputText id="ot1"
                                value="#{employeesVar.FirstName} #{employeesVar.LastName}"/>
              </af:forEach>
          </af:panelGroupLayout>
      </af:panelBox>
    </af:showDetailItem>
  </af:forEach>
</af:panelAccordion> 

Note that the departmentVar variable is not accessible within the body of the second af:forEach component. While it can be used to configure the second af:forEach component data access, the variable cannot be referenced from the af:panelBox within the second af:forEach component

As shown in the page source above, the first af:forEach tag reads the parent information from the ADF tree binding. Make sure you set the iterator binding for the tree binding to -1 for the RangeSize property as otherwise you would only see the first 10 parent row. 

(PageDef file)

<executables>
  <variableIterator id="variables"/>
  <iterator Binds="allDepartmentsWithEmployees" RangeSize="-1" DataControl="AppModuleDataControl"
            id="allDepartmentsWithEmployeesIterator" Refresh="deferred"/>
</executables> 

Sample Download

You can download the Oracle JDeveloper 11g R2 (11.1.2.1) sample from here. Just configure the database connection to point to an Oracle database with the HR schema unlocked. The ADF BC model is configured to only show departments that have employees (using a ViewCriteria)

Which solution to choose ?

And Finally, what's the difference between this declarative approach and the  more code centric solution in ADF Code Corner sample 81? The difference is that this sample fetches all data upon rendering of the panel accordion, which may be expensive if we talk about thousands of child (employee) records. The sample on ADF Code Corner queries the detail data when a panel tab is selected. Sample 81 on ADF Code Corner however doesn't cache the data of previously selected panels and thus always re-fetches detail data. When working with large data sets however he solution on ADF Code Corner performs better.

Tuesday Feb 28, 2012

Accessing WebLogic Server JDBC DataSource from Java in JSF

There may be a requirement for you to access a JDBC data source defined on the WebLogic Server (for example to query a database or database schema other than the one the application's business service is connected with.

To access the JDBC DataSource, for example from a managed bean in JSF, you code like the following:

java.sql.Connection
connection = null;
try {
  javax.naming.Context initialContext = new javax.naming.InitialContext();
  javax.sql.DataSource dataSource = 
            (javax.sql.DataSource)initialContext.lookup("java:comp/env/jdbc/hrconnDS");
  connection = dataSource.getConnection();
  
 } catch(Exception e){
      e.printStackTrace();
      //or handle more gracefully 
 }
In the example above, the JDBC DataSource is defined in WLS as "hrconnDS"

Thursday Feb 23, 2012

Strategies for controlling the af:popup close event

In a previous OTN Harvest summary, I discussed how to handle the af:dialog OK and Cancel event: http://www.oracle.com/technetwork/developer-tools/adf/learnmore/77-ok-cancel-support-in-dialog-351871.pdf . In this post, I get back to this though not covering the "Cancel" case.

There are two options developers have to handle the "Ok" event of an af:dialog component in an af:popup

· Using a DialogListener with the default Ok button

· Using a custom command button instead of the default Ok button

The sample use case is quite simple: A command button added to a page opens the popup component that contains a dialog with an input text field in it. Users can provide a value in the input text field and press either a "custom Ok" button or the default "Ok" button to submit the value and close the popup. In both cases, the provided value in the text field is evaluated and if it is not "ADF ROCKS" written in whatever case, an error message is displayed and the popup is not closed.

Example 1: Using the default OK button and a DialogListener

The sample application opens with a single command button visible on the screen. Pressing the command button uses an af:showPopupBehavior behavior tag to open the popup.

Typing "Hello World" into the text field and pressing the Ok button (which is the default Ok button configured in the Type property of the af:dialog component) invokes a managed bean method that is configured as the DialogListener for the dialog component.

<af:dialog
  …
  binding="#{DialogContentHandler.dialogComponent}"
  contentWidth="300"
  contentHeight="200"
  dialogListener="#{DialogContentHandler.onDialogAction}">

Note that the binding property is set to define a JSF component binding of the dialog to the managed bean to allow the lookup of the input text field in Java. Because the entered text is not "ADF ROCKS", an error message is displayed below the input field To display error messages in a popup, the af:message tag is added to the popup dialog as shown below


<af:dialog …>
  <af:panelFormLayout id="pfl1">
    <af:panelLabelAndMessage label="Validate Me" id="plam1">
     <af:panelGroupLayout id="pgl1" layout="vertical">
        <af:inputText id="it1"/>
          <af:message id="m1" for="it1"/>
        </af:panelGroupLayout>
      </af:panelLabelAndMessage>
  </af:panelFormLayout>
</af:dialog>

The dialog listener managed bean code displaying the error or, in the case of the correct entry, dismissing the dialog is shown next:

/**
 * Dialog Listener that validates the input field for ADF ROCKS. 
 * If the string doesn't match, an error message is shown
 * 
 * @param dialogEvent
 */
public void onDialogAction(DialogEvent dialogEvent) {  
  //lookup the text field starting from the dialog component for
  //which a JSF component binding has been created
  UIComponent inputText = dialogComponent.findComponent("it1");
  String inputTextValue = 
                (String)((RichInputText)inputText).getValue();        
  //If the dialog outcome is OK (the OK button has been pressed)
  //validate the entry
  if(dialogEvent.getOutcome() == DialogEvent.Outcome.ok){
     if(inputTextValue != null &&
         inputTextValue.equalsIgnoreCase("ADF ROCKS")){   
     //ensure the input text value is reset for a second run
     ((RichInputText) inputText).resetValue();
  }
  else{
   //show error message so that popup doesn't close
   FacesContext fctx = FacesContext.getCurrentInstance();
   FacesMessage fm = 
             new FacesMessage(FacesMessage.SEVERITY_ERROR,
                     "Value Validation Failed", "Try: ADF ROCKS");       
   fctx.addMessage(inputText.getId(), fm);
  }
 }
} 

Example 2: Using a custom command button

The af:dialog component renders without default buttons when the Type property is set to none. This is useful if you want to add your own command buttons, which then also are better to customize.

<af:dialog id="…" …>
  <f:facet name="buttonBar">
    <af:commandButton text="Custom OK" id="cb2"                                                  
        action="#{DialogContentHandler.onOK}"                                                    
        partialSubmit="true"/>
</af:dialog>

The use case is the same as before and a message displays when the input text field value is not ADF ROCKS. The only difference to the code executed in a DialogListener is that the command button action listener needs to explicitly close the dialog

public String onOK() {      
  UIComponent inputText = dialogComponent.findComponent("it1");
  String inputTextValue = (String) ((RichInputText)inputText).getValue();
  if(inputTextValue != null &&
     inputTextValue.equalsIgnoreCase("ADF ROCKS")){
     RichPopup rp = (RichPopup)
     dialogComponent.getParent();
     //reset input text component
     ((RichInputText)inputText).resetValue();
     rp.hide();
  }
  else{
    FacesContext fctx = FacesContext.getCurrentInstance();
    FacesMessage fm = 
         new FacesMessage(FacesMessage.SEVERITY_ERROR,
                         "Value Validation Failed", "Try: ADF ROCKS");
         fctx.addMessage(inputText.getId(), fm);
  }        
  return null;
}

Sample Download

You can download an Oracle JDeveloper 11g R2 sample workspace from here: https://blogs.oracle.com/jdevotnharvest/resource/PreventPopupFromClosing.zip. The JSF page has both approaches,default Ok button and custom Ok button on a single dialog. You don't need a database for running the sample.


        
    

Friday Jan 20, 2012

Adding checkboxes to sortable table headers

Using the header facet of the af:column tag, developers can add components to the table header, for example to indicate the component to be excluded from printing or export to Excel. The side effect of adding a command item to a table header is when the table column is sortable.

In this case, selecting the component, for example an instance of af:selectBooleanCheckBox , will also trigger sorting on the table, which you don't want to happen until users explicitly ask for this.

The solution to this problem is JavaScript added to the checkbox components and the use of stopBubbling() to prevent user the click event to reach the table header and then the sort icons.

<af:resource type="javascript">
   function stopBubbling(evt){
      evt.stopBubbling();
  }
</af:resource> 

The JavaScript is called from the checkbox as shown below

<af:selectBooleanCheckbox id="sbc2" text="FirstName"                                                           
                          value="..." autoSubmit="true">
 <af:clientListener method="stopBubbling" type="click"/>
</af:selectBooleanCheckbox>

Tuesday Dec 27, 2011

Using JSTL in ADF Faces

The JavaServer Pages Standard Tag Library (JSTL) provides expressions for common web application functionalities. Though JavaServer Faces Expression Language (EL) is different from JSTL expressions, the two can be used in combination for functional for which it makes sense. For example, the table below lists JSTL expressions that operate on String values.

Function

Description

fn:contains()

Tests if an input string contains the specified substring.

fn:containsIgnoreCase()

Tests if an input string contains the specified substring in a case insensitive way.

fn:endsWith()

Tests if an input string ends with the specified suffix.

fn:escapeXml()

Escapes characters that could be interpreted as XML markup.

fn:indexOf()

Returns the index withing a string of the first occurrence of a specified substring.

fn:length()

Returns the number of items in a collection, or the number of characters in a string.

fn:replace()

Returns a string resulting from replacing in an input string all occurrences with a given string.

fn:startsWith()

Tests if an input string starts with the specified prefix.

fn:substring()

Returns a subset of a string.

fn:substringAfter()

Returns a subset of a string following a specific substring.

fn:substringBefore()

Returns a subset of a string before a specific substring.

fn:toLowerCase()

Converts all of the characters of a string to lower case.

fn:toUpperCase()

Converts all of the characters of a string to upper case.

fn:trim()

Removes white spaces from both ends of a string.

Src: http://www.tutorialspoint.com/jsp/jsp_standard_tag_library.htm

To use JSTL in Oracle ADF Faces component EL references, you need to add the JSTL namespace of the tags you want to reference. For the JSTL functions shown in the table above, you need to manually add the name space highlighted in red to the JSPX document or page fragment.

<jsp:root  
 xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"         
 xmlns:f="http://java.sun.com/jsf/core"         
 xmlns:h="http://java.sun.com/jsf/html"         
 xmlns:af="http://xmlns.oracle.com/adf/faces/rich"        
 xmlns:fn="http://java.sun.com/jsp/jstl/functions">

The value expression below, referenced in an af:outputText component, displays the substring (first, second and third character) for a value read from the ADF binding layer.

<af:outputText value="#{fn:substring(bindings.employeeName.inputValue,1,3)}" id="ot7"/>

Friday Nov 25, 2011

How to detect browser type and version from ADF Faces

Sometimes ADF applications need to know about the user browser type and version. For this, assuming you need this information in Java, you can use the Trinidad RequestContext object. You could also use the AdfFacesContext object for the same, but since the ADF Faces Agent class is marked as deprecated, using the equivalent Trinidad classes is the better choice.

The source code below prints the user browser information to the Oracle JDeveloper message window

import org.apache.myfaces.trinidad.context.Agent;
import org.apache.myfaces.trinidad.context.RequestContext;



RequestContext requestCtx = RequestContext.getCurrentInstance();
Agent agent = requestCtx.getAgent();
String version = agent.getAgentVersion();
String browser = agent.getAgentName();
String platform = agent.getPlatformName();
String platformVersion = agent.getPlatformVersion();

System.out.println("==================");
System.out.println("Your browser information: ");
System.out.println("Browser: "+browser);
System.out.println("Browser Version : "+version);
System.out.println("Browser Platform: "+platform);
System.out.println("Browser Platform Version:
"+platformVersion);
System.out.println("==================");


Thursday Nov 24, 2011

Accessing the JSESSIONID from JSF

The following code attempts to access and print the user session ID from ADF Faces, using the session cookie that is automatically set by the server and the Http Session object itself.

FacesContext fctx = FacesContext.getCurrentInstance();
ExternalContext ectx = fctx.getExternalContext();
HttpSession session = (HttpSession) ectx.getSession(false);
String sessionId = session.getId(); 
System.out.println("Session Id = "+ sessionId); 
Cookie[] cookies = 
 ((HttpServletRequest)ectx.getRequest()).getCookies(); 
//reset session string
sessionId = null; 
if
(cookies != null) {
 for (Cookie brezel : cookies) {
     if (brezel.getName().equalsIgnoreCase("JSESSIONID")) {
       sessionId = brezel.getValue();
       break;
     }
  } 
} 
System.out.println("JSESSIONID cookie = "+sessionId);

Though apparently both approaches to the same thing, they are different in the value they return and the condition under which they work. The getId method, for example returns a session value as shown below

grLFTNzJhhnQTqVwxHMGl0WDZPGhZFl2m0JS5SyYVmZqvrfghFxy!-1834097692!1322120041091

Reading the cookie, returns a value like this

grLFTNzJhhnQTqVwxHMGl0WDZPGhZFl2m0JS5SyYVmZqvrfghFxy!-1834097692

Though both seem to be identical, the difference is within "!1322120041091" added to the id when reading it directly from the Http Session object. Dependent on the use case the session Id is looked up for, the difference may not be important.

Another difference however, is of importance. The cookie reading only works if the session Id is added as a cookie to the request, which is configurable for applications in the weblogic-application.xml file. If cookies are disabled, then the server adds the session ID to the request URL (actually it appends it to the end of the URI, so right after the view Id reference). In this case however no cookie is set so that the lookup returns empty. In both cases however, the getId variant works.

Tuesday Nov 22, 2011

Creating ADF Faces Comamnd Button at Runtime

In ADF Faces, the command button is an instance of RichCommandButton and can be created at runtime. While creating the button is not difficult at all, adding behavior to it requires knowing about how to dynamically create and add an action listener reference. The example code below shows two methods: The first method, handleButtonPress is a public method exposed on a managed bean.

public void handleButtonPress(ActionEvent event){ 
 System.out.println("Event handled"); 
 //optional: partially refresh changed components if command 
 //issued as a partial submit
}

The second method is called in response to a user interaction or on page load and dynamically creates and adds a command button. When the button is pressed, the managed bean method – the action handler – defined above is called. The action handler is referenced using EL in the created MethodExpression instance. If the managed bean is in viewScope, backingBeanScope or pageFlowsScope, then you need to add these scopes as a prefix to the EL (as you would when configuring the managed bean reference at design time)

//Create command button and add it as a child to the parent component that is passed as an 
//argument to this method
private void reateCommandButton(UIComponent parent){
  RichCommandButton edit = new RichCommandButton();
  //make the request partial
  edit.setPartialSubmit(true);
  edit.setText("Edit");                         
  
  //compose the method expression to invoke the event handler
  FacesContext fctx = FacesContext.getCurrentInstance();
  Application application = fctx.getApplication();
  ExpressionFactory elFactory = application.getExpressionFactory();
  ELContext elContext = facesCtx.getELContext();
  MethodExpression methodExpressio = null;
  //Make sure the EL expression references a valid managed bean method. Ensure
  //the bean scope is properly addressed 

  methodExpression =  elFactory.createMethodExpression( 
                            elContext,"#{myRequestScopeBean.handleButtonPress}", 
                            Object.class,new Class[] {ActionEvent.class});
  //Create the command buttonaction listener reference

  MethodExpressionActionListener al = null;        
  al= new MethodExpressionActionListener(methodExpression); 
  edit.addActionListener(al); 

   //add new command button to parent component and PPR the component for 
   //the button to show
   parent.getChildren().add(edit);
   AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance(); 
   adfFacesContext.addPartialTarget(parent); 
}

How-to read data from selected tree node

By default, the SelectionListener property of an ADF bound tree points to the makeCurrent method of the FacesCtrlHierBinding class in ADF to synchronize the current row in the ADF binding layer with the selected tree node. To customize the selection behavior, or just to read the selected node value in Java, you override the default configuration with an EL string pointing to a managed bean method property. In the following I show how you change the selection listener while preserving the default ADF selection behavior.

To change the SelectionListener, select the tree component in the Structure Window and open the Oracle JDeveloper Property Inspector. From the context menu, select the Edit option to create a new listener method in a new or an existing managed bean.

For this example, I created a new managed bean. On tree node select, the managed bean code prints the selected tree node value(s)


import java.util.List;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;

import java.util.Iterator;  
import oracle.adf.view.rich.component.rich.data.RichTree;
import oracle.jbo.Row;
import oracle.jbo.uicli.binding.JUCtrlHierBinding;
import oracle.jbo.uicli.binding.JUCtrlHierNodeBinding;
import org.apache.myfaces.trinidad.event.SelectionEvent;

import org.apache.myfaces.trinidad.model.CollectionModel;
import org.apache.myfaces.trinidad.model.RowKeySet;
import org.apache.myfaces.trinidad.model.TreeModel;  
public class TreeSampleBean {
 public TreeSampleBean() {}
 public void onTreeSelect(SelectionEvent selectionEvent) {
  //original selection listener set by ADF
  //#{bindings.allDepartments.treeModel.makeCurrent}
  String adfSelectionListener = "#{bindings.allDepartments.treeModel.makeCurrent}";
  //make sure the default selection listener functionality is 
  //preserved. you don't need to do this for multi select trees 
  //as the ADF binding only supports single current row selection  
 
  /* START PRESERVER DEFAULT ADF SELECT BEHAVIOR */
  FacesContext fctx = FacesContext.getCurrentInstance();
  Application application = fctx.getApplication();
  ELContext elCtx = fctx.getELContext();
  ExpressionFactory exprFactory = application.getExpressionFactory();
  
  MethodExpression me = null;
  me = exprFactory.createMethodExpression(elCtx, adfSelectionListener, 
                                          Object.class, newClass[]{SelectionEvent.class}); 
  me.invoke(elCtx, new Object[] { selectionEvent });  

  /* END PRESERVER DEFAULT ADF SELECT BEHAVIOR */
  RichTree tree = (RichTree)selectionEvent.getSource();
  TreeModel model = (TreeModel)tree.getValue();   
 
  //get selected nodes
  RowKeySet rowKeySet = selectionEvent.getAddedSet(); 
  Iterator rksIterator = rowKeySet.iterator(); 
  //for single select configurations,this only is called once 
  while (rksIterator.hasNext()) {
    List key = (List)rksIterator.next();
    JUCtrlHierBinding treeBinding = null;
    CollectionModel collectionModel = (CollectionModel)tree.getValue();
    treeBinding = (JUCtrlHierBinding)collectionModel.getWrappedData(); 
    JUCtrlHierNodeBinding nodeBinding = null;
    nodeBinding = treeBinding.findNodeByKeyPath(key);
    Row rw = nodeBinding.getRow();
    //print first row attribute. Note that in a tree you have to 
    //determine the node type if you want to select node attributes 
    //by name and not index 
    String rowType = rw.getStructureDef().getDefName();
  
    if(rowType.equalsIgnoreCase("DepartmentsView")){
      System.out.println("This row is a department: " +  
                         rw.getAttribute("DepartmentId"));
    }
    else if(rowType.equalsIgnoreCase("EmployeesView")){
     System.out.println("This row is an employee: " + 
                         rw.getAttribute("EmployeeId"));
    }     
    else{
      System.out.println("Huh????");  
    }
    // ... do more useful stuff here   
  }   

}

--------------------

Download JDeveloper 11.1.2.1 Sample Workspace


Wednesday Nov 09, 2011

JSF 2.0 Preemptive Navigation in ADFc of JDeveloper 11.1.2

Preemptive navigation is a new feature in JavaServer Faces 2.0 and allows runtime introspection of control flow cases for their target view.The JSF API for this is the ConfigurableNavigationHandler class that exposes the following methods

  •  getNavigationCase(FacesContext context, 
                      java.lang.String fromAction, 
                      java.lang.String outcome) 
  • getNavigationCases() – returns a Map<String, Set<NavigationCase>> that lists all available navigation cases with the viewId as the map keys

  • performNavigation(java.lang.String outcome) – Navigates to the next view based on the outcome. Developers using this method must ensure it is used during JSF InvokeApplication phase as it cannot be used any later

The NavigationCase class wraps the information defined for a navigation, including the condition (also a new feature in JSF 2.0) in which the navigation case is valid.

Preemptive navigation can be used in an application to populate redirect components, like the goLink shown below with a target view, or for redirects in a managed bean, for which developers need to know the target view. The sample below shows an ADF Faces goLink pointing to a managed bean. The managed bean returns the redirect URL for the link to follow when clicked on.

Preemptive Navigation

 The managed bean accesses the NavigationHandler defined for the JSF instance and verifies it to be an instance of ConfigurableNavigationHandler before it looks up the target viewId for the control flow case.

Note that ADFc in Oracle JDeveloper 11g R2 also supports conditional navigation, in which case developers can define an EL expression on the control flow case, using the Property Inspector, that determines when a navigation case is valid and when it is not.

Conditional navigation

Also note that Preemptive navigation fails with a NullPointer exception if the referenced control flow case is conditionally set to disabled. To handle this, the managed bean code above needs to be surrounded with a try…catch block.

Wednesday Oct 19, 2011

How to render different node icons for different tree levels

 Nodes of the af:tree component are stamped, which means that upon rendering, the UI component defined in the nodeStamp facet is printed repeatedly with different data values. If the requirement is to icons differently on each node level, then EL and the af:switcher component help as explained in the following.

<af:tree value="#{bindings.LocationsView1.treeModel}" var="node"                
  selectionListener="#{bindings.LocationsView1.treeModel.makeCurrent}"
  rowSelection="single" id="t1">
  <f:facet name="nodeStamp">
    <af:switcher id="s1" 
                 facetName="#{node.hierTypeBinding.viewDefName}">
      <f:facet name="adf.bc.samples.model.vo.LocationsView">
        <af:commandImageLink text="#{node}" 
              icon="/images/l1.png" id="cil4"
              inlineStyle="height:16px; width:17px;" disabled="true"/>
      </f:facet>
      <f:facet name="adf.bc.samples.model.vo.DepartmentsView">
         <af:commandImageLink text="#{node}" icon="/images/l2.png" 
               id="cil6" 
               inlineStyle="height:16px; width:17px;" disabled="true"/>
      </f:facet>
      <f:facet name="adf.bc.samples.model.vo.EmployeesView">
        <af:commandImageLink text="#{node}" icon="/images/l3.png" 
             id="cil8"
             inlineStyle="height:16px; width:17px;" disabled="true"/>
      </f:facet>
   </af:switcher>
  </f:facet>
</af:tree>

The sample uses the Oracle HR sample schema Locations, Departments and Employees hierarchy. The tree nodes are printed as af:commandImageLinks because this gives me the option to print an icon and text within the same component, avoiding the need for a surrounding container (the nodeStamp facet can only have a single child component) that messes with the tree layout. Because I have no use of the command link action, I set its disabled property to false.

Note: The command image link labels are rendered in gray, which can be corrected to black color using skinning. Alternatively you can look for finding a different UI component to render the node (as said command image link is what worked for me the best).

The an af:switcher component is used to switch between different command link definitions. To distinguish the tree node level, the view definition name of the View Object rendering the node is used. You can read the node view definitions from the tree binding in the PageDef file.

 <tree IterBinding="LocationsView1Iterator" id="LocationsView1">
      <nodeDefinition DefName="adf.bc.samples.model.vo.LocationsView" 
                      Name="LocationsView10">
        <AttrNames>
          <Item Value="LocationId"/>
          <Item Value="City"/>
        </AttrNames>
        <Accessors>
          <Item Value="DepartmentsView"/>
        </Accessors>
      </nodeDefinition>
      <nodeDefinition DefName="adf.bc.samples.model.vo.DepartmentsView" 
                      Name="LocationsView11">
        <AttrNames>
          <Item Value="DepartmentId"/>
          <Item Value="DepartmentName"/>
        </AttrNames>
        <Accessors>
          <Item Value="EmployeesView"/>
        </Accessors>
      </nodeDefinition>
      <nodeDefinition DefName="adf.bc.samples.model.vo.EmployeesView" 
                      Name="LocationsView12">
        <AttrNames>
          <Item Value="EmployeeId"/>
          <Item Value="FirstName"/>
          <Item Value="LastName"/>
        </AttrNames>
      </nodeDefinition>
    </tree> 

For this, select the PageDef file in the Application navigator and open the Structure Window. Expand the tree node binding and select a nodeDefinition. The DefName of this node is shown in the Property Inspector. You use the DefName as a facet name in the af:switcher component, as shown in the page code snippet shown in the beginning.

The af:switcher component uses the facetName property to determine which of the contained facet to display upon rendering, using the following EL: #{node.hierTypeBinding.viewDefName}

The facets defined in the switcher component are named after the View Object definition names they represent. In the sample that is based on the Oracle HR schema, these view object names are

  • adf.bc.samples.model.vo.LocationsView
  • adf.bc.samples.model.vo.DepartmentsView
  • adf.bc.samples.model.vo.EmployeesView

The EL expression used in the switcher facetName property returns one of these strings in which case the facet renders, thus showing different icons. Note that this technique can be used for any use case requiring a difference in the tree node handling or behavior

Friday Sep 16, 2011

How-to launch browser print dialog when showing printable page

The following code, when referenced from the beforePhase property of the f:view component, automatically opens the browser print dialog if a page is rendered as printable. I did write about this topic in the OTN Harvest summary of January 2011. To this time however I used an internal flag, which doesn't feel right. The code used in this post only uses public APIs and thus is a solution that lasts.

public void beforePhaseMethod(PhaseEvent phaseEvent) {
  if (phaseEvent.getPhaseId() == PhaseId.RENDER_RESPONSE){
    FacesContext fctx = FacesContext.getCurrentInstance();
    
    AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
      if(adfFacesContext.getOutputMode()== OutputMode.PRINTABLE){              
         ExtendedRenderKitService erks = null;
         erks = Service.getRenderKitService(fctx, ExtendedRenderKitService.class);
         erks.addScript(fctx, "window.print();");                   
       }
    }             
}

The managed bean method is referenced from the f:view component as follows

<f:view beforePhase="#{SampleBean.beforePhaseMethod}">
  ...
</f:view>

With this listener and code, when the af:showPrintableBehavior tag is used on a command item to show a printable page, the browser print dialog is automatically opened. While the same code also works for page fragments, the f:view tag is only available for JSPX documents. In this case you either set the beforePhase method property on the JSPX document hosting the page fragment, or define a global phase listener (faces-config.xml) that then works for all pages in an application.


Thursday Sep 08, 2011

Adding a clientListener dynamically at runtime using JavaScript

In ADF Faces, the client listener component - af:clientListener - is added to ADF Faces components to listen for component events that should be handled in JavaScript on the client. The following JavaScript function allows you to add a client listener dynamically at runtime, which may become handy when working on client side integration, or use cases that cannot be implemented on the server: for example, adding custom behavior to a command button that is part of an existing ADF Faces component and not exposed in the property palette.

/*
 * componentId - the client component of the ADF Faces component to add the 
                 client listener to. This componentId must reflect naming 
                 containers as the search is absolute
 * eventType   - ADFFaces component event type like 'action','click','select' 
 *               etc. Note that the chosen event type must be supported by the 
 *               component as otherwise the ADF Faces assertion check fails. 
 *               E.g. an inputText field does not have an 'action' event type but 
 *              'focus', 'blur', 'valueChange','mouseOver' etc.
 * newListener - a JavaScript function: function name(evt){} that must exist on the 
 *               page that hosts the component to which the client listener is added. 
 *               The newListener argument is not passed with quotes
 */
function addComponentListener(componentId, eventType, newListener){
  var component = AdfPage.PAGE.findComponentByAbsoluteId(componentId);
  if(component != null){
     component.addEventListener(eventType,newListener);               
  }
}

To add a client listener to a command button 'cb1' that is located in the page (no naming containers) so that next time a users hits the button it shows a hello world alert, you use the following call

addComponentListener('cb1','action',helloWorld); 

The hello world function looks as shown below:

function helloWorld(evt){
  alert(evt.getSource()+' says Hello World');
}

This prints the command button instance with an attached "helloe world" in a JavaScript alert.


        
    
About

The Oracle JDeveloper forum ranks in the Top 5 of the most active forums on the Oracle Technology Network (OTN).



The OTN Harvest blog is a summary of selected topics posted on the OTN Oracle JDeveloper forum.



It is an effort to turn knowledge exchange into an interesting read for developers who enjoy little nuggets of wisdom





Frank Nimphius

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today