JHeadstart

Subscribe to JHeadstart feed
Java EE Consultants - JHeadstart, ADF, JSF
Updated: 14 hours 30 min ago

2 members JHeadstart Team in Oracle Innovation Showcase

Thu, 2009-07-16 00:07

We are honored that 2 of our JHeadstart Team members are included in the Conversations with Oracle Innovators of the Oracle Innovation Showcase.

Full credit to Steven, who is the driving power behind the JHeadstart innovations!

Categories: Development

Core ADF 11: Mapping Multiple FocusViewId's with XMLMenuModel

Thu, 2009-04-16 20:48

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.

Categories: Development

Core ADF 11: Abandoning a Bounded Taskflow with Stand-Alone Pages

Thu, 2009-04-02 22:14

In the first post of this Core ADF 11 series, I discussed various options for designing your page structure. This post applies when you choose to use bounded task flows with stand-alone .jspx pages. If you make these bounded task flows available through a menu where each menu item calls a bounded task flow, you will hit the "cannot abandon bounded taskflow" issue: when you start the home page of your application (located in the unbounded taskflow), and then click on a menu item for the first time, you start the bounded taskflow associated with the menu item. Now, the menu is still visible, and the end user might decide to click on another menu item before he correctly finished the bounded task flow. In other words, he wants to abandon the task flow he started through the first menu item. However, when the user clicks on another menu item, nothing will happen! This is because the control flow rules that start each bounded taskflow are defined in the unbounded taskflow (in adfc-config.xml). They are not available in the context of the first bounded taskflow started by the user.

The "poor man's" solution to this problem is hiding or disabling all menu items and other global actions that will launch a bounded task flow (like "Help" and "Preferences") until the user executes a bounded task flow return action and returns to a page in the unbounded task flow. Not a very user friendly solution....

Another technique is to use a bounded taskflow template for all your bounded task flows. In the template you duplicate all control flow rules and all task-flow call activities that are defined in the unbounded task flow. While this might seem to work at first sight, this is not a viable option. What happens with this approach is that each time you click on another menu item, a new bounded sub task flow is started in the context of the current task flow: in other words, you keep on nesting bounded task flows. This is highly undesirable and will cause problems sooner or later with memory resources and/or transaction management. Since the task flows are nested, taskflow resources are never released, and transactions are never rolled back. Don't do this!

The only proper solution is to execute a return activity to quit the current task flow before starting the new bounded task flow. In this post, I will describe how you can implement this in a generic way which hides the complexity for your developers. The outline of the solution is as follows:

  • The action property of all menu items as defined in the XML menu Model is prefixed with "abandon:" (This is all what your developers need to know)
  • A custom navigation handler is used that checks whether the navigation outcome starts with "abandon:". If so, and the current task flow context is a bounded task flow, then the navigation outcome as specified after the "abandon:" prefix is stored in a request attribute named "callMenuItem", and the super navigation handler is called with the CallMenuItem outcome. If we are not in the context of a bound taskflow, we call the super navigation handler directly with the navigation outcome as specified after the "abandon:" prefix.
  • The CallMenuItem outcome navigates to a generic task flow return activity named "CallMenuItem" which in turn navigates to the CallMenuItem activity in the unbounded taskflow.
  • The CallMenuItem activity in the unbounded taskflow is a "dynamic router" a Java class with a getOutcome method that returns the value of the EL expression passed in as argument. In our case, the router returns the value of the currentMenuItem request attribute, which causes navigation to the end target: either some page in the unbounded task flow, or another bounded task flow call activity in the unbounded task flow.

Here are the detailed steps to implement this:

1. Prefix the value of the action property in all your menu items in XML MenuModel with "abandon:"

2. Create a custom navigation handler with the following code:

public class MyNavigationHandler
  extends NavigationHandlerImpl
{
  public static final String ABANDON_PREFIX = "abandon:";
  private static final String CURRENT_MENU_ITEM = "currentMenuItem";
  private static final String CALL_MENU_ITEM = "CallMenuItem";


  public MyNavigationHandler(NavigationHandler navHandler)
  {
    super(navHandler);
  }


  public void handleNavigation(FacesContext facesContext, String action,
                               String outcome)
  {
    if (outcome != null && outcome.startsWith(ABANDON_PREFIX))
    {
      abandonTaskFlowIfNeeded(facesContext, action, outcome);
    }
    else
    {
      super.handleNavigation(facesContext, action, outcome);
    }
  }


  public void abandonTaskFlowIfNeeded(FacesContext facesContext,
                                      String action, String outcome)
  {
    String strippedOutcome =
      outcome.substring(MyNavigationHandler.ABANDON_PREFIX.length());
    TaskFlowContext tfc =
      ControllerContext.getInstance().getCurrentViewPort().getTaskFlowContext();
    if (tfc.getTaskFlowId() == null)
    {
      // we are not in a bounded task flow, do normal navigation
      handleNavigation(facesContext, action, strippedOutcome);
    }
    else
    {
      String newOutcome = strippedOutcome;
      Map requestMap = 
          FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
      requestMap.put(CURRENT_MENU_ITEM, newOutcome);
      handleNavigation(facesContext, null, CALL_MENU_ITEM);
    }
  }
}

3. Register your custom navigation handler in the faces-config.xml. Go to the Overview tab, click on Application, and set your class name in the navigation handler property.

4. Create a bounded taskflow template used by all your bounded task flows, and add the following code to it:

<task-flow-return id="CallMenuItem">
  <outcome>
    <name>CallMenuItem</name>
  </outcome>
</task-flow-return>
<control-flow-rule>
  <from-activity-id>*</from-activity-id>
  <control-flow-case>
    <from-outcome>CallMenuItem</from-outcome>
    <to-activity-id>CallMenuItem</to-activity-id>
  </control-flow-case>
</control-flow-rule>
</task-flow-template>

When using declarative transaction management, you can add that a rollback should be performed as part of the return activity.

5. Create a Java class named DynamicRouter with the following code:

public class DynamicRouter implements Serializable
{
  public String getOutcome(String outcome)
  {
    return outcome;
  }
}

6. Add the following code to adfc-config.xml (the unbounded task flow):

<method-call id="CallMenuItem">
  <method>#{dynamicRouter.getOutcome}</method>
  <parameter>
    <class>java.lang.String</class>
    <value>#{requestScope.currentMenuItem}</value>
  </parameter>
  <outcome>
    <to-string/>
  </outcome>
</method-call>


<control-flow-rule>
  <from-activity-id>*</from-activity-id>
  <control-flow-case>
    <from-outcome>CallMenuItem</from-outcome>
    <to-activity-id>CallMenuItem</to-activity-id>
  </control-flow-case>
  ...
</control-flow-rule>  


  <managed-bean>
    <managed-bean-name>dynamicRouter</managed-bean-name>
    <managed-bean-class>view.DynamicRouter</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
  </managed-bean>

That's it. You can download a sample workspace that illustrates the solution here. In a later post, we will discuss how to add an alert to warn the end user about unsaved changes when he abandons a task flow.

Categories: Development

Core ADF 11: Designing the Page and Taskflow Structure

Fri, 2009-03-20 00:53

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.

Categories: Development

JHeadstart 11.1.1 Technical Preview Now Available

Thu, 2009-03-05 22:15

Oracle JHeadstart 11.1.1 Technical Preview is now available for download. Customers who own a JHeadstart supplement option license can download it from the Consulting Supplement Option portal.

An evaluation version of JHeadstart 11.1.1 is not yet available. We plan to make an evaluation version available in the fall of calendar year 2009.

In addition to many small enhancements, the following features have been added in JHeadstart 11.1.1:


  • New Application Level in Application Definition Editor: In previous versions, a project typically contained multiple JHeadstart application definition files, that were edited and generated separately. In release 11, these application definition files have been renamed to service definition files, and can be edited and generated all together in the application definition editor. The editor has a new top level application node that shows all services. The application node contains a number of properties that were previously found at the service level, but had to be the same accross all services anyway.

  • Generation of (un)Bounded Task Flows: JHeadstart now uses the new ADF 11 controller facilities that extend the JSF controller functionality. For each top-level group a bounded task flow is generated. Through the new metadata element Group Parameters you can specify specific custom taskflow parameters. By default, the generated task flows already contain a number of parameters to maximize reuse of the task flows. For example, you can set parameters to query or set a specific row as the current row or to start the flow in "create mode" (useful in wizard-style layouts).

  • Ability to Generate Page Fragments and (Dynamic) Regions: Through the new properties (Default) Group Usage and (Default) Group Region Access you can specify how groups are generated. You can continue to generate the groups as stand-alone pages like in version 10.1.3. However, to increase the reusability of your groups, it is recommended to generate them as page fragments within a region. You then have the option to generate one common UI shell page, with all group regions displayed through a dynamic region ("One-page application"), or to generate separate group-specific UI Shell pages.

  • Enhanced Menu Generation: The generated menu now uses the new ADF 11 XML Menu Model, which greatly increases the flexibility of the menu generation, both in terms of menu structure as well as menu layout. The menu structure is specified in an XML file, and is
    separated from the layout of the menu. Using the ADF Faces NavigationPane or Tree elements included in the page templates that ship with JHeadstart, you can configure how the menu is displayed. The menu structure is now integrated accross services as well, allowing you to create a true application-level menu. Through the new application-level property Content First-Level Menu Tabs with allowable values "Base groups current service", "All services", and "All base groups" you can determine which levels of the menu are shown with group pages, and whether menu items of different services are "merged" into one menu level.
    If the declarative properties are not sufficient, you can use custom Velocity templates for the generation of the XML menu model files to easily generate menu structures that introduce new intermediate menu levels, effectively "hiding" the underlying structure of services and groups.

  • Support for Page Templates: JHeadstart ships with sophisticated sample page templates. You can use these default templates, or create your own based on one of the sample templates. Using the new Page Template and Region Template properties you can specify which template is used by the generated page (fragments). You can even make the page templates dynamic, JHeadstart will then generate a drop down list to switch page templates which allows you to change the page template used by a page at runtime. This might be useful in prototyping situations where you you want to evaluate different page designs with end users.

  • Enhanced Support for Deeplinking: Through a new item display value groupLink and associated new property Link Group Name, you can now declaratively set up taskflow calls between groups in any service. Using the new Item Parameters element, you can specify parameters that should be passed into the taskflow of the called group. For example, in the employees group, you can deeplink to the Jobs group, showing the job details of the current employee, by passing the JobId of the current employee as parameter. You can choose whether you want to display the called group in a popup window, or in the base window replacing the calling group. Using group taskflow parameters, that are referenced by the Insert/Update/Delete allowed properties you can easily show the Jobs form page in read-only mode, when deep-linked, and in editable mode when called through the menu.

  • Easy Reuse of Groups using a Group Region: In version 10.1.3, group regions were introduced to provide more control over the layout of detail groups that are displayed on the same page as the master group. In version 11 we have significantly extended the functionality of this concept. By checking the new checkbox property Include as ADF Region, you can select any group from any service to be displayed on the page of the group owning the group region. In other words, the group region is now the key concept to reuse a group across your application. You can reuse the same group in many pages by including it as a group region. Through the new child element Group Region Parameters, you can specify parameters that should be passed into the taskflow of the group. For example, using these new concepts it becomes trivial to have one group "Employees" that is used to generate a top-level page with just employees, and reuse that same group as child group under the Departments group to generate a master-detail page with department and its employees. By using group query bind parameters property, that reference taskflow input parameters, you can implement the "master-detail" synchronization. By using additional task flow parameters, you can hide the "DepartmentId" item, the search area, the Save and cancel buttons in the employees group when used as child of the Departments group.

  • Support for Model-based LOV's: ADF Business Components 11 has extensive support for list of values. Using JHeadstart you can choose to use these new model-based LOV's, or the existing web-tier JHeadstart LOV's. When running the "New Jheadstart Service Definition" wizard you can choose which LOV style you want to use. When choosing model-based LOV's, JHeadstart will auto-generate View Accessors and List-of-Values definitions in the View Objects, based on the view links that exist between the view objects. You can choose whether the View Accessors are based on separate auto-generated read-only View Objects or on the existing entity-based view objects. When choosing read-only View Objects, you can choose to create the view accessors based on a shared application module instance, which allows you to reuse the LOV queries across user sessions, which is useful for static reference tables.

  • LOV Groups Reusable Accross Services: It is now possible to create an LOV item that uses an LOV group that resides in a different service.

  • Ability to generate skin switcherThrough a new application-level property, you can generate a drop down list to switch between all skins registered in trinidad-config.xml at runtime.

  • Enhanced Template Editing: The templates tab of the JHeadstart Application Definition Editor is now context-sensitive. You will only see the templates that apply to the node selected in the tree navigator. Furthermore, you can now create and modify custom templates within the application definition editor. On the Templates tab, you can change the file name of the default template, and when you tab out the modified file name field, a dialog pops up that asks for confirmation to create a custom template based on the default template. When you confirm this action, the custom template will be created, and you can edit it directly in the application definition editor, in a new area at the bottom of the templates tab.

  • JHeadstart Forms2ADF Generator: This utility allows you to reuse Oracle Forms .fmb files to create ADF web applications. The utility creates ADF Business Components based on the blocks, LOV's and Record Groups defined in the form. It also creates a JHeadstart Application Definition file which allows you to generate fully functional web pages based on the form definition. The PL/SQL logic is extracted and displayed as documentation nodes into the Application Definition. The Application Definition Editor includes an option to move the PL/SQL logic to the database. This utility is still lacking some functionality and has not yet proven itself in a real customer project. We invite you to test the JFG and report your findings to us. See the chapter "JHeadstart Forms2ADF Generator' in the JHeadstart Developer's Guide, section "Troubleshooting" for more information on how to send us your feedback. This utility replaces the JHeadstart Designer-Forms Migrator which is no longer available in this release.


For a complete list of all existing features, use this link.

Categories: Development

Variations on ADF BC isAttributeChanged method

Sun, 2009-02-08 22:09

ADF Business Components have a method isAttributeChanged available in Entity Objects that returns true if the the attribute has been changed since it was obtained from the Database. This method can cause a problem when used in business rules that are triggered at Commit time, or in business rules that only apply when the attribute value has actually changed.

Problem when calling isAttributeChanged at Commit time

From the Javadoc of EntityImpl: Note that if a Posting operation is performed, and the attribute value is sync'd with the database, that subsequent requests will return false, until the next time the attribute is changed.

As Jan Kettenis pointed out, this means that if you use isAttributeChanged for a business rule that is triggered at Commit time, it will always return false, even though the attribute was changed. Jan describes a workaround for this problem.

Problem calling isAttributeChanged if value changed back to old value

Another quote from the Javadoc of EntityImpl: Note that this method will return true if the value has been set to the same as the database value since it doesn't perform a value comparison.

This can be a problem for validations that apply only when the value changes: for example, the rule "Department Name cannot be changed if the Department has Employees". You might have implemented the rule in a method validator using isAttributeChanged:

  return (!isAttributeChanged(DEPARTMENTNAME) || !hasEmployees());

Suppose a Department has Employees, and you change both the Department Name and the Department Manager. When you apply these changes, you obviously get an error because you are not allowed to change the Department Name. Suppose you now change the Department Name back to its original value, and try again, because you still want to change the Department Manager. You will still get the error, because isAttributeChanged(DEPARTMENTNAME) will remain false until you have posted the data, even though the attribute value is now the same as the value in the database!

To avoid this, you can define a variation on isAttributeChanged in your EntityImpl base class: isAttributeValueChanged (but note that when called from Commit level, it will still return false like Jan described).

  /**
* Using getPostedAttribute and getAttribute this method retrieves the old and new
* values and compares whether it has changed. Alternative to isAttributeChanged,
* which returns true if the value was modified to the same value as the database.
* @param index index of the attribute.
* @return true if the value of the attribute at the given index
* is different from the one in the database.
*/
public boolean isAttributeValueChanged(int index) {
Object oldVal = getPostedAttribute(index);
Object newVal = getAttribute(index);
boolean valueChanged = true;
if ((oldVal == null) && (newVal == null)) {
valueChanged = false;
} else if (newVal != null) {
if (newVal instanceof String) {
if (((String)newVal).length() == 0) {
if (oldVal == null) {
valueChanged = false;
}
}
}
if (valueChanged && newVal.equals(oldVal)) {
// in new rows, the oldVal is always the same as the newVal,
// but if the value is not null in a new row, we want to return true
if (getEntityState() != Entity.STATUS_NEW) {
valueChanged = false;
}
}
}
return valueChanged;
}

Categories: Development

Oracle launches Rich Enterprise Application (REA) site

Sun, 2009-01-04 18:59

This site is targeted towards Developers that want to build highly interactive web applications that deliver desktop quality of user interface. Oracle's REA technologies consist of Oracle ADF Faces Rich Client and Oracle WebCenter. URL: http://rea.oracle.com.

When you click the "Explore Now" button, you see a very nice tree with topics, where you can flip each node to see a small explanation, or expand it to see new nodes with subtopics.
rea_site.jpg

Categories: Development

ADF 11 Course 12-16 January in The Netherlands

Tue, 2008-12-23 23:06

Oracle University just released the ADF 11 course, covering many new ADF 11 features.
I will be teaching this course from 12 to 16 january 2009 in De Meern, the Netherlands.

There are still seats available. For a list of course topics and registration, use this link.

Categories: Development

Java By Exception for Business Rules in ADF BC

Fri, 2008-11-21 05:31

Oracle ADF Business Components 11g has many new features for declarative business rules, allowing a "Java By Exception" approach. When can and should you use these declarative features, and when and how should you use Java to implement business rules in ADF BC?

  What are Business Rules?

Let me first explain what I mean by business rules in this context.

Business rules are used to preserve data integrity, to prevent inconsistent data from being entered into the database. They are checks and/or data corrections you want to perform in your entity objects, triggered by an insert, update or delete of certain data.

The most common ones are validation rules or constraints: if the data changes do not satisfy a certain integrity rule, you return an error. For example: the End Date may not be before the Start Date. Another category of business rules is change event rules: if a certain change is made, you automatically want another change in the data to be made as well. For example: automatically converting a Code to uppercase (so that the integrity rule that a Code must be uppercase is implicitly maintained).

The white paper Business Rules in ADF BC defines business rules as follows (in the intro of section 6, "Business Rule Classification Scheme"):

A business rule is either

  • a restriction that applies to the state of the system, the change of the system state or the authorized use of the system,
  • or an automatic action that is triggered by a change to the system state
 Naval Safety Center

My Preferred ADF BC 11g techniques for Business Rules

In Oracle JDeveloper 11g, ADF BC offers several new features for declarative validation rules support (see also the JDeveloper 11g New Features list):

  • Control when a validation rule is executed by specifying a conditional execution expression or triggering attributes (or by deferring the execution to transaction level) New!
  • Create parameterized error messages and warnings saved in external resource bundles New!
  • New validation rule types (in the below lists they are tagged with New! )

Let me describe the ADF BC techniques (with links to relevant sections in the Fusion Developer's Guide for ADF or to paragraphs below) that I refer to later in this post, in order of preference. In fact, I have two lists of preferred techniques, depending on the question: Should the rule be implemented for more than one entity?

Preferred Techniques for Rules that apply to Multiple Entities

If the answer is Yes, the rule should be implemented in more than one entity, then my preferred techniques are:

  1. Domain, see Section 34.1 Creating Custom, Validated Data Types Using Domains.
  2. Custom Validator, also known as Registered Rule, where you code a new validator type in Java, which you can then declaratively assign to an entity (or entity attribute). See How to create a Custom Validator.
  3. Overriding superclass method in the EntityImpl java class at the base class level (see Section 33.2 Creating a Layer of Framework Extensions, which you should do before creating any individual Entity Objects), using separate classes and interfaces for the actual business logic (see How to apply OO principles in EntityImpl).
Preferred Techniques for Rules that apply to Individual Entities

If the answer is No, the rule should be implemented in only one entity, then my preferred techniques are:

  1. Entity, Attribute, or Association Property, for example the Mandatory property of an attribute, or the History Column feature for tracking creation / last update info. You can even create your own History Type.
  2. Declarative Validation Rule that doesn't require any Groovy or Java coding (for more information see Section 7.3, Adding Validation Rules to Entity Objects and Attributes). If you need to postpone the calling of the rule to commit time, set "Defer execution to transaction level" when adding the rule to an entity or attribute.
    • Collection New! (validates based on aggregate values in a collection)
    • Compare (performs a logical comparison between the attribute and a value)
    • Key Exists New! (checks cache and database to see if value exists as a key in a certain entity, useful for logical Foreign Keys that are not checked in the database)
    • Length (compares the character or byte length of an attribute value to the specified size)
    • List (compares an attribute against a list of values that can be specified in several ways)
    • Range (tests for attribute values within specified minimum and maximum values)
    • Regular Expression (compares an attribute value against an expression mask)
    • UniqueKey New! (business-logic tier equivalent of a unique constraint in the database)
  3. Script Expression New! (declarative validation rule used for creating Groovy validation expressions). Applicable if the Groovy syntax and constructs, see Section 3.6 Overview of Groovy Support, are powerful enough to implement the rule, and the expression is limited in length.
  4. Method (declarative validation rule used to call any method defined by the entity object that accepts a parameter of the appropriate type and returns a boolean value, see Section 8.2 Implementing Validation and Business Rules Programmatically). If the method for this rule is more than a few lines of Java code, I prefer to implement the rule as a Custom Validator, so it can be implemented in a separate class with several methods.
  5. Custom Validator, also known as Registered Rule, where you code a new validator type in Java, which you can then declaratively assign to an entity (or entity attribute). See How to create a Custom Validator.
  6. Overriding superclass method in EntityImpl java class, at the level of an individual entity object (see Section 8.2 Implementing Validation and Business Rules Programmatically), using separate classes and interfaces for the actual business logic (see How to apply OO principles in EntityImpl).

Now these lists need a little explanation. Why do I prefer these, and when can or can't you use one of these techniques?

Why these techniques?

My preferences for certain techniques are based on the following assumptions:

  • XML-based is preferred to code-based (be it Groovy or Java code). There are several reasons for this:
    1. One benefit of using declarative validation (versus writing your own validation) is that the validation framework takes care of the complexities of batching validation exceptions, which frees you to concentrate on your application's specific validation rule logic. (Quote from Section 7.1 Introduction to Declarative Validation.)
    2. Another benefit of declarative validation, is the traceability of the rules. It is easier to see which rules are implemented in a certain entity. Just open an entity definition, go to the Validations, and see which declarative rules are listed. Even if not all rules can be listed there, it is a good starting point.
    3. In a later version of JDeveloper 11g, when MDS (Meta Data Services) is fully enabled, it will be possible to customize these XML-based business rules without having to change the source files (see chapter 33 of the draft developer's guide of a JDeveloper 11g Preview release).
    4. The recently started ADF Methodology Group discussed the term "Java By Exception" (see Extreme Reusability, an ADF development methodology proposed by Avrom Roy-Faderman). Applications are supposed to be entirely declarative, except for those few cases where declarative uses of the framework do not cover needed functionality. The idea is that you can harness the power of Java when you need it, and avoid the complexity at other times. At the ADF Methodology symposium, Steve Muench, very appropriately, pointed out that ADF, as well, is a "Java by exception" framework.
  • Generalize, Push Up, and Customize : Avrom's Extreme Reusability describes this practice as: Rather than writing a piece of Java code tailored to a highly particular need, consider the possibility of generalizing the code and pushing it up the class hierarchy to a custom framework class, allowing declarative customization of specific cases.
    I agree with this practice: if you expect that the functionality of the Java code is probably going to be needed in another place as well, possibly with a small variation, generalize it. This principle also implies that even if you can implement a rule with a declarative validator, but it needs to be done in more than one entity, then you still should create a generalized implementation so you avoid repeatedly entering the same properties like error message code, validator subtype, etc. Repetitive work increases the probability of mistakes, and the result is hard to maintain.
  • Use Object Oriented Design Principles when coding business rule logic in Java (I took these from the first chapter of the excellent book Head First Design Patterns):
    1. Encapsulate what varies, translated to: put the business rule logic in a separate class - see How to apply OO principles in EntityImpl
    2. Program to interfaces, not implementations, translated to: let the rule class implement an interface which you call from the EntityImpl - see How to apply OO principles in EntityImpl

    This avoids very large EntityImpl classes, in which it is hard to keep track of what was generated by ADF BC and what was added by you. If you follow these principles, there are only a few superclass methods that are likely to be customized (see Section 8.1 Introduction to Programmatic Business Rules).

    Cover of Head First Design Patterns book
When to use what technique?

The preferred techniques mentioned above may or may not be possible, depending on the type of business rule you need to implement. Besides the decision if the rule should be implemented for more than one entity or not, what matters as well is the complexity of the rule, and at which point in the ADF Entity Object's validation cycle it needs to be triggered.

Triggering moment

What you need to remember is that all techniques, except coding directly in the EntityImpl class, can only be called at the time a single Entity row is validated, or at the time all Entity rows are committed (the latter occurs if you defer the execution to transaction level, using a setting in the Entity "Add Validation Rule" dialog). That is the equivalent of overriding the entity superclass methods validateEntity or beforeCommit.

However, sometimes you need to trigger the rule from the superclass methods doDML, remove, or some other method. The Rule Classification Scheme of the Business Rules in ADF BC white paper can help: for the rule types "Delete", "Collection, no parent", "Collection within parent", "Change event with DML", and "Change Event without DML", it might not be possible to trigger them at validate or commit time.

Complexity of the rule

Furthermore, validation rules are often not suitable for change event rules (that trigger a data change in some other attribute or entity). It might interfere with the Validation Cycle (see Section 7.2 Understanding the Validation Cycle).

The complexity of the rule determines if it is possible to implement it using one of the most preferred techniques, like a Compare validator or a Range validator. If that is not possible, it might be possible to implement it using a Groovy Script validator. If that is not possible, it might be possible to implement it with a Method Validator with a small number of lines of Java code in the method, etcetera.

Algorithm to determine Best Rule Technique

If you apply all of these guidelines, you have the following summary algorithm for determining the best rule technique:

Algorithm for choosing ADF BC 11g Business Rule Technique

How to create a Custom Validator

See the Fusion Developer's Guide for Oracle Application Development Framework Section 34.10: Implementing Custom Validation Rules. Essentially you create a new Java class that extends AbstractValidator and implements the JboValidatorInterface. This forces you to implement a method validate(JboValidatorContext). You can specify custom rule properties for the things that vary if you need to implement this rule for more than one entity or attribute.

If the rule must be triggered at commit time as opposed to validate time, you must also implement the JboTransValidatorInterface. This forces you to implement a method validateMany in addition to validate, but in our test case it was never called, even if you set the "Defer execution to transaction level" when assigning the rule to an entity or attribute. Still, a logical implementation of this method would be to loop over all JboValidatorContexts and call validate for each:

    public void validateMany(ArrayList valCtxs) {
        JboValidatorContext evObj;
        for (int i = 0; i < valCtxs.size(); i++)
        {
           evObj = (JboValidatorContext) valCtxs.get(i);
           validate(evObj);
        }
    }

When you have written the Java class, you must register it as a rule in your Model project, and then you can declaratively assign it to one or more entities, possibly setting some entity-specific properties for the rule. Those properties can be exposed simply by creating getters and setters for them in the Java class.

Note: if the rule should be applied to all Entity Objects, without any specific properties to configure for each individual entity, then it is easier to use the technique of Overriding a method in EntityImpl, so you don't have to do any work in the individual entities.

Note 2: You could also use the JboValidatorInterface when the trigger time is not validateEntity or commit, but it requires instantiating a JboValidatorContext like this:

        JboValidatorInterface businessRule = new MyCustomValidator();
        JboValidatorContext entityContext =
            new JboValidatorContext(JboValidatorContext.TYP_ENTITY_OBJECT,
                                    this, getEntityDef().getFullName(), null,
                                    null, this);
        businessRule.validate(entityContext);

I find this less intuitive then the technique described below, and you don't have the added value that declarative rules have like traceability, bundling exceptions, and allowing changes through MDS. Besides, this codes gives a compiler warning, and it is not clear to me what the alternative for the deprecated constructor is:

   constructor JboValidatorContext(int, java.lang.Object, java.lang.String,
   oracle.jbo.AttributeDef, java.lang.Object, java.lang.Object) has been deprecated

It works fine, though, so if you like it you can use this approach instead of the one described in How to apply OO Principles in EntityImpl.

Thank you Steve Muench and Jan Kettenis for helping me to position this technique in 11g.

How to apply OO Principles in EntityImpl

This paragraph explains how I applied the Object Oriented Design Principles "Encapsulate what varies" and "Program to interfaces, not implementations", with the help of my colleague Gaurav Malhotra. I found that it helps separate out the rule implementation code from the Business Components classes, and clarifies the communication between the classes by using interfaces for different types of rules, if you want to use the technique of overriding a method in EntityImpl. In fact, the Custom Validator technique (see How to create a Custom Validator) also applies these OO principles.

Note: if the rule applies to multiple entities but not exactly the same for each entity, you can use Custom Properties in the individual entities to configure your business rule.

Take the rule code out of the EntityImpl class

One of the most complex rules we had in a large project involved a private helper class, several static final constants, and several helper methods that need to be called several times. So you can imagine that it is not a good idea to put all that code in the EntityImpl class, mixed in with the code for other rules and/or the ADF BC framework generated code.

Borrowing a phrase from Antonio García's description of the Strategy Pattern, it is much simpler to keep track of them if each behaviour (in our case business rule) is a separate class, and not buried in the body of some method. That is how I interpreted the principle "Encapsulate what varies".

Inject the Entity Row

Now, in the business rule logic we probably have to call several EntityImpl methods for the row that triggered the rule, like the entity's getters, or the superclass generic getAttribute method. How do you do that if the rule logic is in a separate class?

The answer is: inject the Entity row into the rule class, using a setEntity method. The rule class can then call entity methods by coding getEntity().someMethod(). This way you can also retrieve values of Custom Properties of Entities in case of generic rules that apply to multiple entities: getEntity().getEntityDef().getProperty(customPropertyName).

Define contract with caller using an Interface

Then, apply the "Program to Interfaces" principle by specifying an interface that defines which rule class methods should be callable from the EntityImpl class and which not. If you have multiple rules that have to be implemented in the EntityImpl, you can probably re-use the same interface for several rules.

An example of such an interface is the general-purpose EntityRule interface (note the use of a Java 5 generic method):

public interface EntityRule<T> {
    public void setEntity(T entity);
    public void process();
}

An implementation of this interface might be:

public class MyComplexRuleImpl implements EntityRule<MyEntityImpl>

You call the rule by coding in the appropriate method of your MyEntityImpl class:

     EntityRule myComplexRule = new MyComplexRuleImpl();
     myComplexRule.setEntity(this);
     myComplexRule.process();

Of course you can have multiple RuleImpl classes each representing a different Entity Rule, which can be called from different places in the EntityImpl.

Benefits of this technique

Using this approach you can give complex rules their own class, while still allowing calls to framework methods like the Entity's getters. In the rule class you can throw an exception if the validation fails. The use of an interface makes it clear how to call such a rule, without knowing the inner workings of the rule.

Categories: Development

JDeveloper: Adding Framework Debug Messages Without Changing Source Code

Thu, 2008-10-23 22:47

Today I ran into a problem where I got a NullPointerException in one of the EL-expressions called when processing an ADF Model page definition. The stack trace did not tell me which EL-expression caused the problem, and it could be one of the many expressions used in the page definition itself, or in one of the many JSF managed beans referenced from the page definition. I could set a breakpoint in the ADF source code where the expression was evaluated, but that breakpoint fired about a hundred times for that single page.

The solution I found can be applied to any situation where you cannot easily influence the source code but still want to write some logging about the values of Java variables and method arguments. You can apply it to frameworks for which you have read-only access to the source code, like ADF and JHeadstart.

I didn't realize at first that JDeveloper 10.1.3 and JDeveloper 11 have an interesting debug feature called BreakPoint Actions. You can set breakpoints and configure them in such a way that they don't halt execution, but only write something to the log. That something can be the value of a Java variable or some other Java expression. So I let it write to the log which EL-expressions it was parsing, and then I could easily check which expression was written just before the NullPointerException.

Here is how you can add your own debug message (with values of class variables) to a (framework) class without having to modify any Java code:

  • You go to the source code of the class you want to add a debug message to. In case of ADF, you can get the source code by logging a Metalink Service Request for the Oracle JDeveloper product, see this forum discussion. How to install it is described in the ADF Developer's Guide, section 24.5. In case of JHeadstart, the source code is automatically installed as soon as you have applied JHeadstart in your project.
  • In your JDeveloper project, go to that source code using for example the menu option Navigate to Java Class, keyboard shortcut Ctrl+Minus.
  • Set a breakpoint on a certain line by clicking the line gutter, which causes a red bullet to appear. For example, I chose the topmost line in my NPE stack trace of which I had the source.
    Image of breakpoint bullet in JDeveloper source code line gutter
  • Then right-click the red bullet and choose Edit Breakpoint.
    Context menu showing Edit Breakpoint option
  • In the Edit Breakpoint dialog, click on the Actions tab, uncheck "Halt Execution" and fill in the desired variable name in Expression. In my example, by coincidence the variable I wanted to display was called expression, so in the screen shot you see Expression: expression.
    Actions tab in Edit Breakpoint dialog
  • Then run your project in Debug mode, and reproduce the problem. In my case the log showed this:
    breakpointActionLog.jpg
It turned out I had made a stupid typing error ... but at least now I knew which one.

Categories: Development

Pages