Wednesday Dec 14, 2011

Core ADF11: Building Highly Reusable ADF Task Flows

In this old post in the Core ADF11 series, I explained the various options you have in designing the page and taskflow structure. The preferred approach in my opinion that maximizes flexibility and reusability, is to build the application using bounded taskflows with page fragments. You then have various ways to disclose these task flows to the user. You can use a dynamic region or dynamic tabs, or a human workflow task list, or even have the user add the task flow at runtime using Webcenter Composer. 

To maximize reuse of individual task flows, there are some simple techniques you can apply:

  • Define a set of input parameters that allows you to configure for the various (re)use cases
  • Define a router activity as the default activity to enable reuse-case-based conditional flows
  • Configure use of dynamic iterator binding to use a task flow both as a master region or detail region
  • Configure display properties of UI components based on task flow input parameters so components can be shown/hidden, editable/readOnly, required/optional, etc based on the specific (re)use case

By applying these techniques, you can dramatically reduce the number of task flows you need to build in your project. In this sample application, you can see how you can reuse the same task flow to support the following reuse cases:

  • Show in read-only mode
  • Enable deeplinking from another page to show one specific row 
  • Use as read-only context info in popup
  • Use as master region as well as detail region 
  • Enable deeplinking from external source like e-mail.

Downloads: 

Friday Oct 07, 2011

Core ADF11: UIShell with Dynamic Tabs

Last update on 28-aug-2013 Sample added for JDeveloper 12.1.2, see bottom of post.

In this old post in the Core ADF11 series, I explained the various options you have in designing the page and taskflow structure. The preferred approach in my opinion that maximizes flexibility and reusability, is to build the application using bounded taskflows with page fragments. You then have various ways to disclose these task flows to the user. You can use a dynamic region that displays the taskflow, as described in this post. You can also go for a more advanced user interface pattern that provides multi-tasking capabilities to the end user by dynamically adding tabs that represent independent tasks for the end user. This patterns is described in more detail here and covers more than only the dynamic tabs. Oracle provides a sample implementation named "Oracle Dynamic Tabs Page Template" of this user interface pattern as an extension to JDeveloper  that can be installed through the JDeveloper Help -> Check for Updates option.  While this implementation might be sufficient in your case, I discovered the need for additional related functionality not provided out-of-the-box by this extension, as well as the need for easier customization options. As I started to implement this pattern at various customers I added more and more functionality to my implementation of this pattern that started off with a more or less one-to-one copy from the Oracle extension implementation. The end result is quite different from the Oracle extension, also because I optimized the implementation for JDeveloper 11.1.2, leveraging the new multiregion binding. For my presentation of this topic at Oracle OpenWorld 2011 I created a showcase application that includes all the code and templates for the dynamic tabs implementation and illustrates all the functionalities my customers asked for, including:

  • Open new tabs in various ways: Using a menu, using a global quick search, from within another tab, and conditionally based on a tab unique identifier that is used to check whether there is already a tab open with the same UID.

  • Close a tab in various ways: using the close icon on the tab, using a close tab button inside the tab region, or auto-closing a tab when the user finished the task.
  • Transaction handling: Each tab should have an independent transaction, a tab needs to be marked dirty with a visual indicator indicating the dirty tab state, and a warning should be shown when the user tries to close a tab with pending changes.
  • Miscelleanous requirements: Update browser window/tab title based on currently selected tab, initially display tabs, prevent tabs from being closed automatically, set maximum number of tabs the user is allowed to open, and update of the tab label based on the current data shown in the region.

If you have similar requirements, then this sample application might be useful to you. You can download the application below, feel free to copy and paste the infrastructure classes, templates and declarative components to your own application. For an explanation of the implementation, see the slides of my OOW presentation.

Downloads: 

Update 13-nov-2012: The implementation in the sample applications has been improved. It is no longer needed to define the managed bean for a tab that is initially displayed in view scope rather than request scope. By improving this implementation of initially displayed tabs, the following issues have been fixed:

  • Closing and re-opening an initially displayed tab caused an NPE (11.1.1.x only)
  • It was not possible to launch another tab from within an initially displayed tab

Update 28-aug-2013: Dynamic Tabs Sample For JDeveloper 12.1.2

Initially, I had problems getting the sample to work in JDev 12.1.2. I logged the following bugs against 12.1.2 that could be reproduced when opening  the 11.1.2 sample in 12.1.2:

  • Bug 17158398 - COMMAND COMPONENTS DO NOT WORK WHEN OPENING REGION IN DYNAMIC TAB
  • Bug 17156672 - UI NOT RENDERED CORRECTLY DUE TO CONTEXT PARAM DEFAULT_DIMENSION
  • Bug 17156560 - JAVA.LANG.ILLEGALSTATEEXCEPTION: COULD NOT FIND COMPONENT TO STREAM
  • Bug 17158597 - UPDATES ARE LOST WHEN DEFINING REGIONCONTROLLER IN PAGEDEF CONTROLLERCLASS PROP

Product development has investigated some of these bugs, and this is the result:

  • The behavior described in bug 17156560 was caused by the fixed value specified for the id property in the af:region tag. Unlike JSP, facelets does not ensure uniqueness when stamping out multiple components, in this case multiple regions inside the af:forEach loop. By removing the id property (facelets will then auto-generate a unique id), or making it unique using an EL expression like id="reg_${vs.index}"  the issue was resolved. See this blog post from Duncan Mills for more info.
  • After applying the fix for bug  17156560 , the behavior reported in bugs 17158398 and 17158597 was no longer seen. These bugs are now closed as "not a bug".
  • The work around for bug 17156672 is simple. The top-level af:decorativeBox in the page template should have the dimensionsFrom property set to "parent" when the DEFAULT_DIMENSIONS context param is set to 'auto' in web.xml. However, since JDeveloper should ensure upwards compatibility, it should never automatically add the DEFAULT_DIMENSIONS web.xml context param to an existing application. So, the bug has been changed to a design-time bug to prevent this from happening. 
With these fixes, the 12.1.2 sample is now working correctly and can be downloaded here.

Tuesday Sep 13, 2011

How to become an Oracle ADF expert in one week (or in 1 day if you don't have so much time)

Oracle ADF is an extremely powerful framework for developing SOA-based web applications. Once you have grasped the key concepts of ADF, it is a real pleasure to work with ADF, and to experience how fast you can build really sophisticated stuff. But......as with any advanced framework, there is a learning curve involved, and I have to admit the ADF learning curve might be experienced as rather steep at times. I am often asked, "how long does it take to become a good ADF developer?", and I usually answer "something between 3-6 months".  The problem is that most project managers don't allow you this time, so you simply start your first ADF project, and find out along the way that you should have chosen a fundamentally different approach to make you application components truly flexible, easy to maintain, and reusable.

Well, Oracle has a one-time-only-unique-offer that allows you to become an ADF expert in one week, and get your ADF app right the first time: Attend Oracle Open World 2011, 2-6 october in San Francisco! The 2011 version of OOW has been proclaimed by the ADF EMG as "Year of the ADF developer" and with good reason. There are lots of good ADF sessions at OOW 2011, a complete overview of all ADF sessions can be found here.

If your employer does not allow you a whole week off from your project, you can go for the sunday-shortcut to become an ADF expert in even one day. The ADF EMG has put together an impressive list of speakers and topics, who will all present on sunday october in Moscone room 2000. See you there! (And don't forget to attend my own presentations on sunday 2.00 PM and wednesday 10.15 AM )

Thursday Jul 28, 2011

JDev 11.1.2: Differences in Table Behavior

While building a simple ADF application in JDev 11.1.2 I encountered some strange runtime behavior. I built another application with the same behavior in exactly the same way in JDev 11.1.1.4 and there things worked smoothly. However, in JDev 11.1.2, the addRow and deleteRow functions didn't work as expected. In this post I will share my tough journey in founding out what was happening, and discuss the difference in behavior and the changes required to make it work in JDev 11.1.2.

When using the add row button (the green plus icon in the screen shot below) an error message for the required JobId dropdown list was shown immediately.

Some investigation using the Google Chrome Developer tools revealed that two requests instead of one are sent to the server, with apparently the second request causing the validation error to appear. (Although, the validation error is a client-side error, so still not sure how the second request can trigger the error.)

At first I thought this was caused by the partialSubmitproperty on the addRow button, which was set to true. Setting this property to false (or removing this property) fixed this problem, but caused table rendering to hang. Weird, but I didn't investigate that further. I decided to build the same app in JDev 11.1.1.4 which worked smoothly and then opened this app in JDev 11.1.2. After the auto-migration, I ran the app but much to my surprise the "Selection required" message didn't show up. I compared the page and page definition of both apps over and over again, and couldn't see any difference.  Eventually, I started comparing all the files in both projects. This lead me to the adf-config.xml file, located in the .adf directory under the root directory of the application, also visible under the Resources panel. In this file, one property existed in the JDev 11.1.2 application that was not present in the JDev 11.1.1.4 version: changeEventPolicy="ppr".

By removing this property, things started to work again, and only one request was sent again.

Note that the really tricky thing here is that when you upgrade an application from JDev 11.1.1.4 this property does not get added, but new JDev 11.1.2 apps will have this property setting, causing difference in behavior between a migrated app and a new app. At this point, my recommendation is to remove this property (or set it to none) for new JDev 11.1.2 apps. If memory serves me well, in some JDev 11.1.1.x version, dragging and dropping a data a table on a page, added the changeEventPolicy="ppr" property to the iterator binding in the page def. In a later JDev 11.1.1.x release this property was gone again. Looks like it is back in a different form (this time in adf-config.xml), but still with undesirable implications. 

The next error I hit was in the delete confirmation dialog, when trying to delete a row. Regardless of which button I pressed (Yes or No), I got validation errors on the underlying new row, and the dialog was not closed, nor was the row removed.

Now, I think this error has to do with the ADF Faces optimized JSF lifecycle.  Since the table needs to be refreshed when the row is removed by clicking yes in the dialog, the af:table component  requires a partialTrigger property that refers to the af:dialog element. With this partialTrigger property in place the ADF JSF optimized lifecycle causes the table items to be submitted (and validated) as well when clicking the Yes or No button in the dialog. Now,I am speculating here, but may be this wasn't supposed to work at all in JDev 11.1.1.4, but it did because of a bug in the optimized lifecyle code, that has now been fixed in JDev 11.1.2...?

Anyway, what feels like the most logical and easy way for me to solve this issue, is setting the immediate property on the af:dialog to true, so the dialog listener method would skip the JSF validation phase. However, the af:dialog element does not have such a property (logged enhancement request).  Two other solutions remain:

  • No longer use the dialoglistener property, but instead define custom Yes/No buttons using the toolbar facet on the af:dialog. On these buttons I can set the immediate property to true, bypassing client-side and server-side validation.
  • Do not specify the af:dialog as partial trigger on the af:table component, instead, add the table or a surrounding layout container element as partial target programatically after deleting the row. This is the solution I chose, since it only required one line of code in the managed bean class that deletes the row.

Links and references:

Thursday Jul 07, 2011

Core ADF11: UIShell with Menu Driving a Dynamic Region

In this old post in the Core ADF11 series, I explained the various options you have in designing the page and taskflow structure. The preferred approach in my opinion that maximizes flexibility and reusability, is to build the application using bounded taskflows with page fragments. You then have various ways to disclose these task flows to the user. You can use an dynamic tabs as described in this post. You can also embed the taskflows using a dynamic region in a page. The application menu then drives the content of the dynamic region: clicking a menu option will load another taskflow in the dynamic region.

 The initial drawback of this approach is that it adds some additional complexity:

  • You can no longer use standard JSF navigation with your menu, there are no pages to navigate to, only regions
  • The XMLMenuModel, an easy way to define your menu structure in XML, cannot be used as-is. The selected menu entry when using the XMLMenuModel is based on the current page, and in our design, the whole application consists of only one page, the UIShell page.
  • The dynamic region taskflow binding should contain a list of all parameters of all taskflows that can be displayed in the dynamic region.

    This is a rather ugly design, and the developer of the UIShell page would need to know all the parameters of all taskflows that might be displayed in the dynamic region. A cleaner implementation is to use the parameter map property against the taskflow binding. However, when using the parameter map, you need to do the housekeeping of changed parameter values yourself when you want the taskflow to refresh when parameters change. In other words, just specifying Refresh=ifNeeded on the taskflow binding no longer works because ADF does not detect changes in a parameter map.

Fortunately, you can address this complexity quite easily by creating some simple, yet powerful infrastructure classes that hide most of the complexity from your development team. (The full source of these classes can be found in the sample application, download links are at the bottom of this post)

  • A DynamicRegionManager class that keeps track of the current task flow and current parameter map 
  • A TaskFlowConfigBean for each task flow that contains the actual task flow document path, the task flow parameters and a flag whether the parameter values have been changed.
  • A RegionNavigationHandler that subclasses the standard navigation handler to provide JSF-like navigation to a region taskflow.
  • A RegionXMLMenuModel class that subclasses the standard XMLMenuModel class to ensure the proper menu tab is selected based on the currently displayed taskflow in the dynamic region. 

The following picture illustrates how this UIShell concept works at runtime.

The UIShell page (with extension .jsf in JDeveloper 11.1.2 and with extension .jspx in JDeveloper 11.1.1.x) contains a dynamic region. The taskflow binding in the page definition of UIShell gets the currently displayed taskflow from the DynamicRegionManager class that is registered as a managed bean under the name mainRegionManager. The actual task flow id and task flow parameters are supplied by the TaskFlowConfigBean. The DynamicRegionManager manages the current taskflow based on a logical name, for example Jobs. When method setCurrentTaskFlowName is called on the DynamicRegionManager with value Jobs (and we will later see how we use the RegionNavigationHandler to do this), the DynamicRegionManager looks up the corresponding TaskFlowConfigBean by suffixing the Jobs task flow name with TaskFlowConfig.he methods getCurrentTaskFlowId and getCurrentParamMap and currentParamMapChanged then obtain and return the correct values using the JobsTaskFlowConfig bean.

Using this technique, you can configure your dynamic region completely declarative, there is no need to write any Java code. All you need to do is configure the mainRegionManager and task flow config beans, as shown below. 

In this sample, I configured the required managed beans in adfc-config.xml. When using ADF Libraries that contain task flows you want to show in the UIShell dynamic region, it is more elegant to define the TaskFlowConfigBean inside the ADF library. I usually define this bean in the adfc-config file of the task flow itself, below the  <task-flow-definition/> section.  It needs to be outside this section, otherwise the bean cannot be found by the DynamicRegionManager bean that is defined in the unbounded task together with the UIShell page. The advantage of this approach is that the config bean is placed in the same file as the task flow it refers to, the disadvantage is that beans defined outside the </task-flow-definition> section are not visible in the overview tab of a bounded task flow.

To set the current task flow name on the DynamicRegionManager, we use a custom RegionNavigationHandler class that you register in the faces-config.xml.

This class extends the default navigation handler, and overrides the handleNavigation method. The code is shown below. If the outcome contains a colon, the part after the colon contains the task flow name that should be set in the dynamic region.


With this class in place, we can use the action property on command components again to do region navigation, just like we are used to for normal JSF navigation, or navigation between page fragments inside a bounded task flow. The complete flow is as follows:

  • The menu item has the action property set to uishell:Jobs.
  • The region navigation handler set the current task flow name on the Dynamic region manager to Jobs (and navigates to UIShell page if needed) 
  • The dynamic region manager picks up the current task flow id and current parameters from the JobsTaskFlowConfig bean.

The last part of the puzzle is the RegionXMLMenuModel class that subclasses the standard XMLMenuModel class. This class overrides the getFocusRowKey method to return the focus path based on the current task flow region name.

With this class defined in the menu model managed bean, the menu will correctly display the selected tab.   

In a future post I will discuss how the same concepts can be applied to a UIShell page with dynamic tabs.

Downloads:

Thursday Jun 23, 2011

Some More New ADF Features in JDeveloper 11.1.2

The official list of new features in JDeveloper 11.1.2 is documented here. While playing with JDeveloper 11.1.2 and scanning the web user interface developer's guide for 11.1.2, I noticed some additional new features in ADF Faces, small but might come in handy:

  •  You can use the af:formatString and af:formatNamed constructs in EL expressions to use substituation variables. For example: 
    <af:outputText value="#{af:formatString('The current user is: {0}',someBean.currentUser)}"/>
    See section 3.5.2 in web user interface guide for more info.

  • A new ADF Faces Client Behavior tag: af:checkUncommittedDataBehavior. See section 20.3 in web user interface guide for more info. For this tag to work, you also need to set the  uncommittedDataWarning  property on the af:document tag. And this property has quite some issues as you can read here. I did a quick test, the alert is shown for a button that is on the same page, however, if you have a menu in a shell page with dynamic regions, then clicking on another menu item does not raise the alert if you have pending changes in the currently displayed region. For now, the JHeadstart implementation of pending changes still seems the best choice (will blog about that soon).

  • New properties on the af:document tag: smallIconSource creates a so-called favicon that is displayed in front of the URL in the browser address bar. The largeIconSource property specifies the icon used by a mobile device when bookmarking the page to the home page. See section 9.2.5 in web user interface guide for more info. Also notice the failedConnectionText property which I didn't know but was already available in JDeveloper 11.1.1.4.

  • The af:showDetail tag has a new property handleDisclosure which you can set to client for faster rendering.

  • In JDeveloper 11.1.1.x, an expression like #{bindings.JobId.inputValue} would return the internal list index number when JobId was a list binding. To get the actual JobId attribute value, you needed to use #{bindings.JobId.attributeValue}. In JDeveloper 11.1.2 this is no longer needed, the #{bindings.JobId.inputValue} expression will return the attribute value corresponding with the selected index in the choice list.
Did you discover other "hidden" new features? Please add them as comment to this blog post so everybody can benefit. 

Monday Sep 27, 2010

UIShell with Dynamic Tabs: Marking the Current Tab Dirty

THIS POST IS OBSOLETE, THE TECHNIQUE DESCRIBED BELOW HAS SOME ISSUES. A NEW AND BETTER IMPLEMENTATION IS DISCUSSED HERE:  http://blogs.oracle.com/jheadstart/entry/core_adf11_uishell_with_dynamic

The Dynamic Tabs UI Shell Template includes an API to mark the current tab as dirty or clean.
By marking a tab as dirty, the tab label will appear in italics, and when closing the tab, the end user will get an alert that pending changes will be lost. Nice functionality, but it is left to the ADF developer to write custom code to call this API.

In the upcoming 11.1.1.3 release of JHeadstart, we added support to automatically call this API by checking the data control state of the current tab. This is done using a custom phase listener, that contains the following code:

package oracle.jheadstart.controller.jsf.listener;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import oracle.adf.controller.internal.binding.DCTaskFlowBinding;
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCDataControl;

import oracle.adf.view.rich.context.AdfFacesContext;

import oracle.binding.BindingContainer;

import oracle.ui.pattern.dynamicShell.Tab;
import oracle.ui.pattern.dynamicShell.TabContext;

import org.apache.log4j.Logger;

/**
* JHeadstart JSF PhaseListener.
* When using Dynamic tabs template, the current tab state is set to dirty or clean,
* depending on the state of the taskflow data control of the current tab
*/
public class JhsPhaseListener
implements PhaseListener
{

private static Logger sLog = Logger.getLogger(JhsPhaseListener.class);

public static final String PAGE_TEMPLATE_BINDING = "pageTemplateBinding";

@Override
/**
* While refreshing the curent tab seems more effective to do only just before render response
* it turns out that when closing a tab, and then returning to a dirty tab, the data control
* of the dirty tab is no longer seen as dirty, so we do it after the three phases that can
* change the state of the current tab: apply request values, update model values and invoke application
*/
public void afterPhase(PhaseEvent event)
{
if (event.getPhaseId() == PhaseId.APPLY_REQUEST_VALUES
|| event.getPhaseId() == PhaseId.UPDATE_MODEL_VALUES
|| event.getPhaseId() == PhaseId.INVOKE_APPLICATION)
{
checkCurrentTabDirtyState();
}
}

@Override
public void beforePhase(PhaseEvent event)
{
}

@Override
public PhaseId getPhaseId()
{
return PhaseId.ANY_PHASE;
}

public void checkCurrentTabDirtyState()
{
TabContext tabContext = TabContext.getCurrentInstance();
if (tabContext == null || tabContext.getSelectedTabIndex() < 0)
{
return;
}
BindingContainer bc =BindingContext.getCurrent().getCurrentBindingsEntry();
if (bc==null)
{
return;
}
DCBindingContainer pageTemplateBc =
(DCBindingContainer) bc.get(PAGE_TEMPLATE_BINDING);
if (pageTemplateBc == null)
{
return;
}
DCTaskFlowBinding tfb =
(DCTaskFlowBinding) pageTemplateBc.get("r" + tabContext.getSelectedTabIndex());
if (tfb == null || tfb.getExecutableBindings() == null ||
tfb.getExecutableBindings().size() == 0)
{
return;
}
DCBindingContainer taskFlowBc =
(DCBindingContainer) tfb.getExecutableBindings().get(0);
DCDataControl dc = taskFlowBc.getDataControl();
if (dc==null)
{
// no data control, we cannot detect pending changes
return;
}
boolean isDirty = dc != null && (dc.isTransactionDirty() || dc.isTransactionModified());
// calling covenience method markCurrentTabDirty adds content area as partial target,
// causing any popups currently displayed to be hidden again.
// Therefore retrieve current tab instance and call setDirty directly
// tabContext.markCurrentTabDirty(isDirty);
Tab tab = tabContext.getTabs().get(tabContext.getSelectedTabIndex());
if (tab.isDirty() != isDirty)
{
sLog.debug("Setting dirty state of dynamic tab with index " +
tab.getIndex() + " to " + isDirty);
tab.setDirty(isDirty);
AdfFacesContext.getCurrentInstance().addPartialTarget(tabContext.getTabsNavigationPane());
}
}
}

Just copy and past this code to create your own phase listener class, and register the phase listener in faces-config.xml like this (substitute with your own class name):


<lifecycle>
<phase-listener>oracle.jheadstart.controller.jsf.listener.JhsPhaseListener</phase-listener>
</lifecycle>

And make sure that the taskflows that are accessed through the dynamic tabs have the data-control-scope element set to shared.
That's all you need to do to enjoy this nice functionality.

Sunday Sep 26, 2010

New: Reusable ADF Taskflows Generator!

[Read More]

Thursday Apr 16, 2009

Core ADF 11: Mapping Multiple FocusViewId's with XMLMenuModel

In the first post of this Core ADF 11 series, I discussed various options for designing your page structure. This post applies when you use stand-alone .jspx pages. If you make these pages available through a menu (af:navigationPane) using XMLMenuModel where each menu item navigates to a page (either in the unbounded task flow or by calling a bounded task flow), the menu tab will be automatically highlighted when you set the focusViewId property of the itemNode in the XML Menu Model file to the name of the .jspx page that appears when you click on the menu item. Unfortunately, you can specify only one focusViewId for an itemNode. If your .jspx page has a button or link to navigate to a secondary page, you will loose the menu item highlighting because the focusViewId of the menu itemNode no longer matches the view id of the current (secondary) page. Even worse, if you have nested itemNodes defined in the XML Menu Model, the submenu that was displayed with the primary page will disappear when navigating to the secondary page.

The "poor-man's solution" to this problem is adding nested item nodes to the XML Menu Model for the detail pages. This technique is easy but can only be applied when there is no page-specific submenu that should be displayed with the pages. You simply create child itemNodes in the XML Menu model for the detail pages in the task flow, which will never be displayed as menu items because you do not include an af:navigationPane that displays this menu level.

A more sophisticated solution, that allows for page-specific submenus, is to subclass XMLMenuModel. The subclass looks up a MenuViewIdMapping managed bean that maps all viewIds of the secondary pages in the task flow to the viewId of the primary page that is defined as focusViewId in the XML Menu Model file.

Here are the steps to implement this technique:

1. Create a subclass of org.apache.myfaces.trinidad.model.XMLMenuModel and override method getFocusRowKey as follows:

  public Object getFocusRowKey()
  {
    // first check whether default behavior results in focus of some menu item 
    Object focusRowKey = super.getFocusRowKey();          
    if (focusRowKey!=null)
    {
      return focusRowKey;
    }
    Map> focusPathMap = getViewIdFocusPathMap();
    // check whether there is a menu mapping for the current viewId
    Map viewIdMapping = getViewIdMapping();
    String viewId = FacesContext.getCurrentInstance().getViewRoot().getViewId();
    if (viewIdMapping!=null && viewIdMapping.get(viewId)!=null)
    {
      String mappedViewId = (String) viewIdMapping.get(viewId);
      List focusPath = focusPathMap.get(mappedViewId);
      return focusPath != null? focusPath.get(0): null;                  
    }
    return null;
  }

Add the following helper method:

  public Map getViewIdMapping()
  {
    ExpressionFactory ef = 
      FacesContext.getCurrentInstance().getApplication().getExpressionFactory();
    ELContext context = FacesContext.getCurrentInstance().getELContext();
    return (Map) ef.createValueExpression(context
                                          ,"#{pageFlowScope.MenuViewIdMapping}"
                                          , Map.class).getValue(context);
  }

2. Change the menu bean property managed-bean-class to reference your subclass:

  <managed-bean>
    <description>Menu Model Managed Bean</description>
    <managed-bean-name>root_menu</managed-bean-name>
    <managed-bean-class>view.MyXMLMenuModel</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
      <property-name>createHiddenNodes</property-name>
      <value>false</value>
    </managed-property>
    <managed-property>
      <property-name>source</property-name>
      <property-class>java.lang.String</property-class>
      <value>/WEB-INF/root_menu.xml</value>
    </managed-property>
  </managed-bean>

3. In each taskflow that can be launched from a menu item, add a managed bean named "MenuViewIdMapping" that defines the mapping between secondary pages and the page defined as focusViewId in the XML Menu Model. Here is an example:

  <managed-bean>
    <managed-bean-name>MenuViewIdMapping</managed-bean-name>
    <managed-bean-class>java.util.HashMap</managed-bean-class>
    <managed-bean-scope>pageFlow</managed-bean-scope>
       <map-entries>
         <map-entry>
           <key>/task-flow-definition1/BoundedTaskFlow1DetailPage</key>
           <value>/task-flow-definition1/BoundedTaskFlow1Page</value>
         </map-entry>  
         <map-entry>
           <key>/task-flow-definition1/BoundedTaskFlow1AnotherDetailPage</key>
           <value>/task-flow-definition1/BoundedTaskFlow1Page</value>
         </map-entry>  
       </map-entries>
  </managed-bean>

That's it. You can download a sample workspace that illustrates the solution here. In a later post, we will discuss the solution for the "one-page application" design with dynamic regions. In that scenario the correct menu item should be highlighted based on the currently selected dynamic region.

Thursday Mar 19, 2009

Core ADF 11: Designing the Page and Taskflow Structure

JDeveloper's tagline is "productivity with choice". In previous releases this choice merely applied to the various technologies that could be used in each of the ADF layers. With release 11 this has changed. When choosing the "Fusion" technology stack within ADF (ADF Faces, ADF Task Flows and ADF Business Components), there are many new and powerful concepts and techniques available that allow you to build the required functionality in different ways. Concepts like (un)bounded taskflows, page fragments, (dynamic) regions, page templates, declarative components, declarative transaction support, XML Menu Model, ADF Faces Layout Components and ADF Libraries open up a wide range of new possibilities to build the view and controller layers.

These new possibilities imply that new design decisions must be made on how to build the required functionality. This is not a trivial task. Many of the concepts relate to each other, and choosing how to use one concept can have impact on the usage of other concepts. I admit that's pretty vague, but in this new "Core ADF 11" series, we (the JHeadstart team) will try to make this more concrete. In this series we will share with you the experiences, issues, challenges, design considerations, design decisions and best practices that we faced/encountered/developed when building JHeadstart release 11.

Note that we will assume that you have a basic understanding of the new ADF 11 concepts. If this is not the case, we recommend you take the ADF 11 course from Oracle University, or if you prefer self-study, take a look at the ADF Learning Center.

In this first post, we will start at the beginning: you want to build your first "serious" ADF 11 page, and you immediately need to choose: do I create a page or a page fragment, do I base the page (fragment) on a template, and do I put the page in an unbounded or bounded task flow.

Page Templates
This one is easy: we recommend to always use a page template. Design the template whatever way you want but make sure you include all the UI elements that need to be displayed on each and every page. Typical content of a page template include a branding image/application logo, global actions like Home, Help, Log Out, Preferences, the menu of the application (either a tree menu, or various levels of navigation panes like tabs, buttons or lists) and a footer area with for example a copyright message. Use page template attributes to pass in properties that might differ between pages like the page title, and to conditionally show/hide parts of the page template for specific pages. In a later post, we will go into more details on designing page templates.
Note that you can also dynamically change the page template associated with a page at runtime. Instead of hardcoding the page template path, you can use an EL expression that binds to a look and feel bean that contains the current page template path.

Bounded or Unbounded Taskflows
Each ADF 11 web application has one unbounded taskflow, and can have one or more bounded task flows. So, the choice is not so much about using bounded or unbounded task flows, but about putting a JSF page inside the unbounded taskflow, or inside a bounded task flow.
When building transactional applications, bounded task flows have major advantages over unbounded task flows. For a start, bounded task flows support additional features that can be very useful when building transactional applications:

  • Save for Later
  • Declarative Transaction Support
  • Exception Handling
  • Own Memory Scope
  • Back button handling

In addition, bounded task flows allow you to benefit from concepts well-known in the world of object oriented programming and component-based development. Using bounded task flows:

  • you can create truly reusable user interface components through page fragments and regions;
  • with a well-defined service contract; through taskflow parameters you can configure the behavior of the taskflow;
  • that are loosely coupled; to use a task flow, you only need to know about the available task flows parameters. The internal flow, structure of user interface components and data structures can be changed without any impact on the pages or other taskflows that are using/calling the taskflow.

In short, well-designed bounded task flows have many advantages over the unbounded task flow. We recommend to only use the unbounded task flow for introductionary pages like the home page or a UI shell page when using regions (see below). All application uses cases should be implemented using a bounded task flow. In a later post in this series, we will discuss in more detail how to design your task flows for maximum reusability.

Stand-alone Pages or Page Fragments with (Dynamic) Regions
For your page design, you basically have three choices

  • Create stand-alone .jspx pages
  • Create .jsff page fragments within a region, and create a shell page for each region that contains the af:region tag.
  • Create .jsff page fragments within a region, and create one common UI shell .jspx page, that uses a dynamic region to display the requested region. This is the "One Page Application" model, where the UI shell page is the only .jspx page in the application, and a menu displayed in the shell page is used to set the current region displayed on the page.

Some considerations and implications to take into account for each choice:
Stand-alone pages:

  • Stand-alone pages are easiest to build and understand, it is like building your app the "10.1.3 way".
  • Stand-alone pages offer little reuse, they cannot be used inside regions
  • When you are building a menu-driven application, and each menu entry starts a bounded taskflow with stand-alone pages, there is no easy way to abandon the current task flow. When the user wants to leave the current task flow and clicks on another menu entry, nothing will happen! This is because the control flow case to start the taskflow associated with the other menu item is defined in the unbounded task flow, which is not reachable once a bounded taskflow is started. The easy solution is to not use bounded task flows at all. You will lose all the advantages of bounded task flows, but when simplicity is more important to you then reusability, this is a valid choice. We discuss a more sophisticated solution to the "abandon taskflow with stand-alone pages issue" in this post of the ADF 11 Core series.
  • When you are using the XMLMenuModel to create your menu, you can associate one focusViewId with each menu item. If you navigate to the page with this view id, the associated menu item will be highlighted automatically. However, if you then navigate to a detail page from the focusViewId page, you loose the menu item highlighting. In this post, we discuss how you can extend the XMLMenuModel to associate multiple pages with one menu item

Static regions with shell page:

  • With this design, you leverage the reuse options of bounded task flows with page fragments
  • You do not run into the XMLMenuModel multi-page problem, since each menu item is associated with the shell page which remains the same while navigating between the page fragments within the ADF region embedded in the shell page
  • You do not run into the "abandon task flow" issue that occurs with stand-alone pages, since the shell pages are all located in the unbounded task flow.
  • If you navigate away to another shell page, you need to be aware that ADF will auto-execute some logic to clean up the abandoned task flow of the region in the previous shell page. The actual clean-up logic executed depends on the transaction settings of the task flow. For example, if the task flow started a new transaction, a rollback will be performed. Note that when using shared data controls, the rollback might have a performance impact since already executed queries in other task flows will need to be re-executed as well. In a later post, we will discuss taskflow transaction management in more detail.

One-page application:
With the previous design option, a shell page is created for each ADF region. Typically, all these shell pages will be very thin and very similar: they are based on the same page template that renders the menu and other common page components, the only difference is the name of the ADF region that is embedded in the shell page. In a one page application, we create one UI Shell page and use a dynamic region to display the correct region. Here are the implications:

  • Performance increases because partial page rendering can be used to replace the content of the dynamic region, the UI shell page does not need to be reloaded. In our experience, the performance difference is quite significant. However, according to the documentation there is a feature called Partial Page Navigation which would allow for a similar performance when using separate shell pages or stand-alone pages. I did not get this feature to work though.
  • The one-page application raises a problem with XMLMenuModel, opposite to the stand-alone pages design: there is only one page used by all menu items. This can be solved by creating a subclass of XMLMenuModel that highlights the correct menu item based on the currently selected region as set in the dynamic region bean. We will discuss this technique in more detail in a future post.
  • When using a dynamic region, the parameters for all region task flows can be defined against the dynamic task flow binding in the page definition of the UI shell page. This quickly becomes ugly and unmanageable: one long list of parameters for all task flows in the application. A better approach is to use the parameter map property of the dynamic task flow binding, and reference the parameter map of the current region through the dynamic region bean. This technique is discussed in more detail in this post.

A variation of this implementation is the use of dynamic tabs rather than a dynamic region. This enables multi-tasking for the end user, see this post for more details on the implementation of dynamic tabs.  

Summary
While JDeveloper makes it easy to later on convert stand-alone pages to fragments, and unbounded task flows to bounded task flows, it is better to make these design decisions upfront. The choices you make have impact on the usage of XMLMenuModel, menu-driven navigation in general, reusability, transaction management and performance.
We recommend to use bounded task flows with page fragments to maximize reusability. While traditional transactional applications are mostly menu-driven, composite, task-oriented user interfaces are likely to become mainstream in the future. When you start building your ADF 11 app today using (dynamic) regions accessible from a menu, you can reuse the same task flows when building more task-oriented user interfaces using the WebCenter and Human Workflow capabilities planned for JDeveloper 11 release 2.

About

Java EE Consultants - JHeadstart, ADF, JSF

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