Thursday Jun 21, 2012

Partial Submit vs. Auto Submit

Partial Submit

ADF Faces adds the concept of partial form submit to JavaServer Faces 1.2 and beyond. A partial submit actually is a form submit that does not require a page refresh and only updates components in the view that are referenced from the command component PartialTriggers property. Another option for refreshing a component in response to a partial submit is call AdfContext.getCurrentInstance.addPartialTarget(component_instance_handle_goes_here)in a managed bean. If a form contains required fields that the user left empty invoking the partial submit, then errors are shown for each of the field as the full form gets submitted.

Autosubmit

An input component that has its autosubmit property set to true also performs a partial submit of the form. However, this time it doesn't submit the entire form but only the component that triggers the submit plus components referenced it in their PartialTriggers property.

For example, consider a form that has three input fields inpA, inpB and inpC with autosubmit=true set on inpA and required=true set on inpB and inpC.

use case 1: Running the view, entering data into inpA and then tabbing out of the field will submit the content for inpA but not for inpB and inpC. Further more, none of the required field settings on inpB and inpC causes an error.

use case 2: You change the configuration of inpC and set its PartialTriggers property to point to the ID of component inpA. When rerunning the sample, entering a value into inpA and tabbing out of the field will now submit the inpA and inpC fields and thus show an error for the missing required value on inpC.

Internally, using autosubmit=true on an input component sets the event root to just this field, which good to have in case of dependent field validation or behavior. The event root can extended to include other components by using the Partial Triggers property on these components to point to the input field that has autosubmit=true defined.

PartialSubmit vs. AutoSubmit

Partial submit set on a command component submits the whole form and leaves it to the developer to decide which UI component is refreshed in response. Client side required field validation (as well as the server side equivalent) is not disabled by executed in this scenario. Setting immediate=true on the command item to skip validation doesn't help as it would also skip the model update.

Auto submit is a functionality on the input components and also performs a partial form submit. However, in addition an event root is defined that narrows the scope for the submitted data and thus the components that are validated on the request.

To read more about this topic, see: http://docs.oracle.com/cd/E23943_01/web.1111/b31973/af_lifecycle.htm#CIAHCFJF

When JDeveloper IDE doesn't render the visual editor

Though with Oracle JDeveloper 11g the problem of the IDE not rendering JSF pages properly in the visual editor has become rare, there always is a way for the creative to break IDE functionality. A possible reason for the visual editor in JDeveloper to break is a failed dependency reference, which often is in a custom JSF PhaseListener configured in the faces-config.xml file. To avoid this from happening, surround the code in your PhaseListener class with the following statement (for example in the afterPhase method)

public void afterPhase(PhaseEvent phaseEvent) {
  if(!ADFContext.getCurrent().isDesigntime()){
    ... listener code here ...
  }
}

The reason why the visual editor in Oracle JDeveloper fails rendering the WYSIWYG view has to do with how the live preview is created. To produce the visual display of a view, JDeveloper actually runs the ADF Faces view in JSF, which then also invokes defined PhaseListeners. With the code above, you check whether the PhaseListener code is executed at runtime or design time.If it is executed in design time, you ignore all calls to external resources that are not available at design time.

OEPE with ADF binding support available: Total Eclipse

The current release of Oracle Enterprise Pack for Eclipse, though in technology preview, brings Oracle ADF binding to the Eclipse IDE. You can download the Software from the link below:

Oracle Enterprise Pack for Eclipse (12.1.1.1.0) Technical Preview New June 2012
Certified on Windows 7/XP/Vista, MacOS, and Linux. Supported on JDK 6.

For many Eclipse users, ADF is new and therefore I expect them to need guidance and help in case they run into issues they don't know how to recover from. Similar, ADF users familiar with Oracle JDeveloper that want to give OEPE a try, will find things different in Eclipse and thus may have questions. 

For both audiences I suggest to post issues to the OEPE forum on the Oracle Technology Network: I'll extend my OTN monitoring to include the OEPE forum on a daily basis to learn about developer needs, requirements and - of course - to catch bugs that need to be filed. From my side this is a part-time involvement, which means that the more ADF questions show on the forum, the more help I could need in answering them. The OTN forum for JDeveloper in my opnion wouldn't be the right place to go to unless the question is a generic ADF question that is not dependent on the integration in Eclipse.

Here's the OEPE forum link for a start

https://forums.oracle.com/forums/forum.jspa?forumID=578

Frank


Wednesday Jun 20, 2012

Free Advanced ADF eCourse, part II

A second part of the advanced Oracle ADF online training is available for free. Lynn Munsinger and her team worked hard to deliver this second part of the training as close to the release of the first installment as possible. This two part advanced ADF training provides you with a wealth of advanced ADF know-how and insight for you to adopt in a self-paced manner.

Part 1 (though I am sure you have this bookmarked):

http://tinyurl.com/advadf-part1

Part 2 (the new material):
http://tinyurl.com/advadf-part2

Quoting Lynn:

"This second installment provides you with all you need to know about regions, including best practices for implementing contextual events and other region communication patterns.

It also covers the nitty-gritty details of building great looking user interfaces, such as how to work with (not against!) the ADF Faces layout components, how to build page templates and declarative components, and how to skin the application to your organization’s needs. It wraps up with an in-depth look at layout components, and a second helping of additional region considerations if you just can’t get enough.

Like the first installment, the content for this course comes from Product Management. This 2nd eCourse compilation is a bit of a “Swan Song” for Patrice Daux, a long-time JDeveloper and ADF curriculum developer, who is retiring the end of this month. Thanks for your efforts, Patrice, and bon voyage!"

Indeed: Great job Patrice! (and all others involved)

Monday Jun 18, 2012

Pretty URL in ADF Faces of JDeveloper 11.1.2.2

Many features planned for Oracle JDeveloper 12c find their way into current releases of Oracle JDeveloper 11g R1 and JDeveloper 11g R2. One example of such a feature is "pretty URL" - or "clean URL" as the Oracle JDeveloper 11g R2 (11.1.2.2) documentation puts it.

"A.2.3.24 Clean URLs

Historically, ADF Faces has used URL parameters to hold information, such as window IDs and state. However, URL parameters can prevent search engines from recognizing when URLs are actually the same, and therefore interfere with analytics. URL parameters can also interfere with bookmarking.

By default, ADF Faces removes URL parameters using the HTML5 History Management API. If that API is unavailable, then session cookies are used.

You can also manually configure how URL parameters are removed using the context parameter oracle.adf.view.rich.prettyURL.OPTIONS. Set the parameter to off so that no parameters are removed. Set the parameter to useHistoryApi to only use the HTML5 History Management API. If a browser does not support this API, then no parameters will be removed. Set the parameter to useCookies to use session cookies to remove parameters. If the browser does not support cookies, then no parameters will be removed."

See: http://docs.oracle.com/cd/E26098_01/web.1112/e16181/ap_config.htm#ADFUI12856

So basically, what this part in the documentation says is:

  • In JDeveloper 11g R2 (11.1.2.2), Oracle ADF Faces automatically removes its internally used dynamic parameters from the URL
  • You can influence the setting with the prettyURL.OPTIONS context option, which however is not recommended you to do because the default behavior is able to detect if the browser client supports HTML 5 History management or not. In the latter case it the uses a session cookie and if this doesn't work, falls back to the "old" URL parameter adding.
The information that is not so explicit and clearly mentioned in the documentation is that this is only for ADF Faces parameters (such as _afrLoop, Adf-Window-Id, etc.), but not the ADF controller token (_adf.ctrl-state)! Removing the ADF controller token is an enhancement request that will be implemented in Oracle JDeveloper 12c

af:inputSlider doesn't render in popup for FF, Safari and Chrome

A problem reported on OTN is that the af:inputSlider component of Oracle JDeveloper 11.1.2.2 doesn't show on all browsers except IE when the slider is added as the sole component in a popup. The problem reproduces with the ADF Faces component demo and I filed bug 14207690. The work around, posted by OTN user "Tses" is to set the inlineStyle property on the slider to table

<af:inputNumberSlider ... inlineStyle="display:table;"/>

Tuesday Jun 12, 2012

Programmatically disclosing a node in af:tree and af:treeTable

A common developer requirement when working with af:tree or af:treeTable components is to programmatically disclose (expand) a specific node in the tree.

If the node to disclose is not a top level node, like a location in a LocationsView -> DepartmentsView -> EmployeesView hierarchy, you need to also disclose the node's parent node hierarchy for application users to see the fully expanded tree node structure. Working on ADF Code Corner sample #101, I wrote the following code lines that show a generic option for disclosing a tree node starting from a handle to the node to disclose.

The use case in ADF Coder Corner sample #101 is a drag and drop operation from a table component to a tree to relocate employees to a new department. The tree node that receives the drop is a department node contained in a location. In theory the location could be part of a country and so on to indicate the depth the tree may have. Based on this structure, the code below provides a generic solution to parse the current node parent nodes and its child nodes.

The drop event provided a rowKey for the tree node that received the drop. Like in af:table, the tree row key is not of type oracle.jbo.domain.Key but an implementation of java.util.List that contains the row keys. The JUCtrlHierBinding class in the ADF Binding layer that represents the ADF tree binding at runtime provides a method named findNodeByKeyPath that allows you to get a handle to the JUCtrlHierNodeBinding instance that represents a tree node in the binding layer.

CollectionModel model = (CollectionModel) your_af_tree_reference.getValue();
JUCtrlHierBinding treeBinding = (JUCtrlHierBinding ) model.getWrappedData();
JUCtrlHierNodeBinding treeDropNode = treeBinding.findNodeByKeyPath(dropRowKey);

To disclose the tree node, you need to create a RowKeySet, which you do using the RowKeySetImpl class. Because the RowKeySet replaces any existing row key set in the tree, all other nodes are automatically closed.

RowKeySetImpl rksImpl = new RowKeySetImpl();
//the first key to add is the node that received the drop
//operation (departments).            
rksImpl.add(dropRowKey);    

Similar, from the tree binding, the root node can be obtained. The root node is the end of all parent node iteration and therefore important.

JUCtrlHierNodeBinding rootNode = treeBinding.getRootNodeBinding();

The following code obtains a reference to the hierarchy of parent nodes until the root node is found.

JUCtrlHierNodeBinding dropNodeParent = treeDropNode.getParent();
//walk up the tree to expand all parent nodes 
while(dropNodeParent != null && dropNodeParent != rootNode){
   //add the node's keyPath (remember its a List) to the row key set
   rksImpl.add(dropNodeParent.getKeyPath());   
   dropNodeParent = dropNodeParent.getParent();
}

Next, you disclose the drop node immediate child nodes as otherwise all you see is the department node. Its not quite exactly "dinner for one", but the procedure is very similar to the one handling the parent node keys

ArrayList<JUCtrlHierNodeBinding> childList = (ArrayList<JUCtrlHierNodeBinding>) treeDropNode.getChildren();                     
for(JUCtrlHierNodeBinding nb : childList){
  rksImpl.add(nb.getKeyPath());
}

Next, the row key set is defined as the disclosed row keys on the tree so when you refresh (PPR) the tree, the new disclosed state shows

tree.setDisclosedRowKeys(rksImpl); 
AdfFacesContext.getCurrentInstance().addPartialTarget(tree.getParent()); 

The refresh in my use case is on the tree parent component (a layout container), which usually shows the best effect for refreshing the tree component. 

Monday Jun 11, 2012

Solving the context menu problem with drag and drop in trees

The following drag-and-drop problem has been reported on OTN: An ADF Faces tree component is configured with a af:collectionDropTarget tag to handle drop events. The same tree component also has a context menu defined that is shown when users select the tree with the right mouse button. The problem now was - and I could reproduce this - that the context menu stopped working after the first time the tree handled a drop event. The drag and drop use case is to associate employees from a table to a department in the tree using drag and drop.

The drop handler code in the managed bean looked up the tree node that received the drop event to determine the department ID to assign to the employee. For this code similar to the one shown below was used

List dropRowKey = (List) dropEvent.getDropSite();
//if no dropsite then drop area was not a data area
if(dropRowKey == null){
   return DnDAction.NONE;
}                

tree.setRowKey(dropRowKey);
JUCtrlHierNodeBinding dropNode = (JUCtrlHierNodeBinding) tree.getRowData();

So what happens in this code? The drop event contains the dropSite reference, which is the row key of the tree node that received the drop event. The code then sets the key to the tree in a call to getRowDate() returns the node information for the drop target (the department). This however causes the tree state to go out of synch with its model (ADF tree binding), which is known to cause issues.

In this use case the issue caused by this is that the context menu no longer shows up. To fix the problem, the code needs to be changes to read the current row key from the key, then perform the drop operation and at the end set the origin (or model) row key back

//memorize current row key
Object currentRowKey = tree.getRowKey();        
List dropRowKey = (List) dropEvent.getDropSite();
//if no dropsite then drop area was not a data area
  if(dropRowKey == null){
    return DnDAction.NONE;
  }              
tree.setRowKey(dropRowKey);
JUCtrlHierNodeBinding dropNode = (JUCtrlHierNodeBinding) tree.getRowData();
... do your stuff here ....
//set current row key back
tree.setRowKey(currentRowKey);
AdfFacesContext.getCurrentInstance().addPartialTarget(tree);

Note the code line that sets the row key back to its original value.

Wednesday May 30, 2012

Problem with deleting table rows using ctrl+a for row selection

The following code is commonly shown and documented for how to access the row key of selected table rows in an ADF Faces table configured for multi row selection.

public void onRemoveSelectedTableRows(ActionEvent actionEvent) {
   RichTable richTable = … get access to your table instance …
   CollectionModel cm =(CollectionModel)richTable.getValue();
   RowKeySet rowKeySet = (RowKeySet)richTable.getSelectedRowKeys();        
  
   for (Object key : rowKeySet) {
      richTable.setRowKey(key);
      JUCtrlHierNodeBinding rowData = (JUCtrlHierNodeBinding)cm.getRowData();
      // do something with rowData e.g.update, print, copy
   }
   //optional, if you changed data, refresh the table      
   AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
   adfFacesContext.addPartialTarget(richTable);  
   return null;
}

The code shown above works for 99.5 % of all use cases that deal with multi row selection enabled ADF Faces tables, except for when users use the ctrl+a key to mark all rows for delete. Just to make sure I am clear: if you use ctrl+a to mark rows to perform any other operation on them – like bulk updating all rows for a specific attribute – then this works with the code shown above. Even for bulk row delete, any other mean of row selection (shift+click and multiple ctrl+click) works like a charm and the rows are deleted.

So apparently it is the use of ctrl+a that causes the problem when deleting multiple rows of an ADF Faces table. To implement code that works for all table selection use cases, including the one to delete all table rows in one go, you use the code shown below.

public void onRemoveSelectedTableRows(ActionEvent actionEvent) {
  RichTable richTable = … get access to your table instance …
  CollectionModel cm = (CollectionModel)richTable.getValue();
  RowKeySet rowKeySet = (RowKeySet)richTable.getSelectedRowKeys();

  Object[] rowKeySetArray = rowKeySet.toArray();   

  for (Object key : rowKeySetArray){           
    richTable.setRowKey(key);
    JUCtrlHierNodeBinding rowData = (JUCtrlHierNodeBinding)cm.getRowData();                           
    rowData.getRow().remove();
  }
  
  AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();          
  adfFacesContext.addPartialTarget(richTable);
}

Solving File Upload Cancel Issue

In Oracle JDeveloper 11g R1 (I did not test 11g R2) the file upload component is submitted even if users click a cancel button with immediate="true" set. Usually, immediate="true" on a command button by-passes all modle updates, which would make you think that the file upload isn't processed either. However, using a form like shown below, pressing the cancel button has no effect in that the file upload is not suppressed.

Update Aug 29/2012: Appears that all the af:subform suggestions in the original blog entry did not work out for any who tried it. The form still appears to be submitted as a whole. To effectively cancel an upload form, use the af:goButton or af:goLink as the cancel button or lnk, which does not submit the form at all.

Tuesday May 29, 2012

Common mistake when iterating rows

In a blog entry from 2010, Oracle's Jobinesh Purushothaman writes about exceptions that are getting raised after developers iterate the collection of a RichTable instance using calls to setRowKey(key).

http://jobinesh.blogspot.co.uk/2010/02/common-mistake-while-iterating-through.html

The reason for the exception is that the selected row state is change on the table without synchronization to happen with the ADF binding layer. The failure of synchronizing the table selected row state with the ADF binding layer is because no row selection event is raised on the table if the row currency is changed programmatically. In theory, if you call #{bindings.treebinding.treeModel.makeCurrent}using a method expression in EL

The failed binding synchronization is because no row selection event is fired when the selected row state is changed on the table using Java in a managed bean.

A solution to this problem could be to call #{bindings.treebinding.treeModel.makeCurrent}from a method expression in Java each time you called setRowKey(key). But, assuming the iteration of rows in a table is for aggregating numeric values or to compare two rows, do you really want to change the current row in the ADF binding layer for each row you need to access?

A better implementation for this use case in my opinion is to read the data from the ADF binding layer instead of the RichTable table instance, which then will not change the row currency at all.

RichTable table = ... get table reference ...
Iterator iter = selectedRowKeys.iterator();
CollectionModel model = (CollectionModel ) table.getValue();
JUCtrlHierBinding treeBinding = (JUCtrlHierBinding )model.getWrappedData();
if (iter != null && iter.hasNext()) {
  Object rowKey = iter.next();
  JUCtrlHierNodeBinding rowData = treeBinding.findNodeByKeyPath(rowKey);     
  //Do something here

}

Note that the reported problem in Jobinesh's blog is for ADF bound tables configured for single row selection. Still I would prefer my approach over changing the selected row state on a table.


Friday May 11, 2012

Guess What - We Want You!

Grant Ronald and the team are looking for help. Grant put the following job posting out on the Internet today

The ADF Product Management team are looking for a strong ADF technologist to join people like Frank Nimphius, Chris Muir and Lynn Munsinger as part of the ADF Product Management team tasked with ensuring customers are able to successfully adopt and implement ADF applications.

Full details of the job post can be found from the link below, or search on IRC1793732 at
https://irecruitment.oracle.com/

https://irecruitment.oracle.com/OA_HTML/OA.jsp?page=/oracle/apps/irc/candidateSelfService/webui/VisVacDispPG&akRegionApplicationId=821&transactionid=30911342&retainAM=Y&addBreadCrumb=S&p_svid=1793732&p_spid=1846054&oapc=8&oas=6vQ7kqKngmX1WWhMqxxszQ
..

For this role we are primarily looking for the role to be US based.


If you feel you have the ADF skills, knowledge and desire to help others be successful with ADF then I look forward to your application.

I see Chris Muir, who recently joined our team, still smiling: So I am convinced this offer is a good one.

Frank

Tuesday May 08, 2012

Managing component value state correctly

A frequent problem reported on OTN is about failure in saving component values using managed beans especially when multi-select components are used. Usually the problem arises when developers store select choice components values in managed beans they use for JSF component bindings as well (which then makes the managed bean becoming a backing bean). Backing beans however are saved in request scope (at least they should to not err again).

Request scope beans however reset their state after each request, which means that no user update in a select component is persisted beyond the request. The result is that user changes are not displayed in the UI or value change listeners don't execute.The proper way of handling the combination of backing beans and component values saved in a managed bean is to come up with a strategy in which the component value is written to a separate bean in a broader scope (I usually recommend viewScope) than the backing bean. The backing bean (request scope) would then access the bean in the larger scope using a ValueExpression or managed bean property to access the user selected data.

I was about to write this solution up in more detail but then found the following blog entry by Duncan Mills:

https://blogs.oracle.com/groundside/entry/the_uimanager_pattern

In his The UIManager Pattern blog entry, Duncan documented the same problem from a different angle. The programmer mistake Duncan observed was that component binding references (the Bindings property reference on an ADF Faces component) were stored in managed beans with a scope larger than request, which JavaServer Faces component don't support.

 In his write-up Duncan documents the same strategy I was about to document, which is to separate the value saving from the component reference using different managed beans in different scope. Well, I think that Duncan's blog entry saved me some time I today.

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.


        
    
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
« July 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
31
  
       
Today