Reference: Create, Read, and Update Data using the ADF UI Shell and EJB

This topic contains the following sections:

Problem Description

The Oracle Dynamic Tabs Shell page template (also known as the ADF UI Shell) supports a tabbed document interface wherein a user can simultaneously work on different tasks at the same time with a single browser window. By itself the template does not show a reference implementation of how to use it in a real ADF application.

Technical Pattern Description

This document is a step by step guide on how to use the ADF UI Shell with an EJB/JPA back end. The end result will allow a user to view, create, and edit data simultaneously in different UI Shell tabs. Through this pattern, you will see how to make effective use of the ADF UI Shell to facilitate a rich user interaction with your underlying reusable ADF task flows. Hopefully, this will also provide inspiration for building your own applications. The technologies used in this pattern are ADF Task Flows, ADF Faces RC, and EJB/JPA The prototype presented herein is based on the HR schema that ships with the Oracle XE Database.

The User Experience

The following screenshots provide an overview of the functionality the application will give the user.

Figure 1:User clicks Jobs link to open the Overview tab within the ADF UI Shell

Overview tab when user clicks on jobs link

When the user opens the application, the option to click on the “Jobs” link is presented. When the user clicks this link, the Overview tab is launched within the ADF UI Shell, displaying the list of jobs in a table, based on the job-list-task-flow. From the Overview tab, the user has the option, for each job listed, to view the associated record in read only mode, or edit the record. Clicking one of the Job Id links in the Overview tab will launch a new tab based on the job-details-task-flow.

Figure 2: Detailed display of the selected record from the Overview tab.

Detailed view of the selected record

The new tab is designed to display the details of the selected record in either read-only or edit mode depending on how the task flow was invoked. In view mode this prevents the user from making inadvertent changes. In this tab the user has two options: edit or done. Clicking Done will close the current tab.

Figure 3: Detailed display of the selected record in edit mode.

Edit mode of the record

Alternatively if the user clicks the Edit button, this sets the form into edit-mode. In edit-mode the user can modify the current record. Thereafter the user has two options: Save and Close; or Cancel.

Figure 4: Modified records marked as dirty will set the tab title to italic.

Dirty tab indicates the user done modification with italic title of the tab

After modifying any attribute and existing the field, the current tab is marked as dirty (unsaved/modified) and the tab title is set to italic to reflect this fact. Clicking Save and Close updates the record and closes the current tab.

Figure 5:Users can edit a selected record from the Overview tab (Edit), or create a new record from the Overview tab (New)

Edit the selected data by Edit tab and can synchronize data by Refresh tab

Alternatively, the user can edit by selecting the record in the table and clicking the Edit button in Overview tab. The Refresh button facilitates synchronizing the list from the database. This is particularly useful if there are some other users who are also working on the same table data. If not, which seems rare in an enterprise context, automated refresh could occur as some users would expect it. This is done via ADF contextual events. However, in the interest of keeping this pattern sufficiently focused, that along with other design choices (e.g., security) was not added.

Figure 6: An asterisk appends the tab title to signify new unsaved data.

Creating a new record by clciking new button asterisk indicates new record with unsaved data

Clicking the new button opens the job-details-task-flow as a new tab ready to edit a brand new record. Prior to loading the tab, the form is initialized to have a default title of “Default Job Title”. An asterisk appends the tab title to signify new unsaved data in this tab.

Figure 7: Collapse the splitter for the left navigation area to provide more screen real estate to the tab region.

After collapse of left splitter will get more space for multiple tabs

The user can open multiple tabs of the same task flow definition from the same Jobs table. Further, the user can collapse the splitter for the left navigation area to provide more screen real estate to the tab region. Each tab may have its own state: read-only, edit, or new. The user can change the row selection in the Overview table without affecting the view of any opened tab.

The Artifacts

The demonstration you will build uses the following artifacts. Note you will build these step by step in the next sections so this section is included as quick summary to what you will create:

A. JPA Entity Model Objects

  1. Job.java - An entity that was automatically generated by JDeveloper through the Entity-From-Tables wizard
  2. 
       
    public class Job implements Serializable {
     @Id
     @Column(name="JOB_ID", nullable = false, length = 10)
     private String jobId;
     @Column(name="JOB_TITLE", nullable = false, length = 35)
     private String jobTitle;
     @Column(name="MAX_SALARY")
     private Long maxSalary;
     @Column(name="MIN_SALARY")
     private Long minSalary;
     //getters and setters...
    }
    
    

B. EJB Session Beans

  1. HRFacadeBean.java
  2. 
       
    //package, imports, and entity annotation statements
    public class HRFacadeBean implements HRFacade, HRFacadeLocal {
     @PersistenceContext(unitName="uiShellModel")
     private EntityManager em;
    
     public Job persistJob(Job job) {
      em.persist(job);
      return job;
     }
    
     public Job mergeJob(Job job) {
      return em.merge(job);
     }
    
     public Job findJobById(String jobId) {
      if (STRING_EMPTY_OBJECT_ID.equals(jobId)){
       return new Job();
      }
      return em.find(Job.class, jobId);
     }
    
     /** <code>select o from Job o</code> */
     public List findAllJobs() {
      return em.createNamedQuery("findAllJobs").getResultList();
     }
    }
    
    
    

    The findJobById method above returns an empty Job object if the jobId input parameter matches a predetermined constant with the value "EMPTY"

C. ADF Task Flows

  1. job-list-task-flow - This task flow constitutes the Overview tab. The task flow contains a single activity, the job_list.jsff which displays a table showing all the jobs.
  2. job-details-task-flow - This task flow constitutes the Jobs tab and is used to support record viewing, editing, and creation. Of importance is the specification of the data control scope for this task flow as isolated. This allows multiple instances of the jobs task flow to open as separate tabs and operate independently.
  3. The task flow takes two parameters. The first is jobId which nominates which task flow to display; and alternatively if null, to create a new job. The second parameter, editMode, indicates for an existing job whether to set a read-only view mode or allow the user to edit the job specified.

    Figure 9: The "job-details-task-flow" is set as isolated by unchecking the “Share data controls with calling task flow” checkbox.

    The job-details-task-flow is set as isolated by unchecking the Share data controls with calling task flow checkbox.

    The "job-details-task-flow" is set as isolated to prevent concurrency issues especially in new record creation. It takes resources, but on the other hand it prevents re-querying of data from the service layer every time the user navigates between tabs of the same task-flow definition.

    This task flow has the following activities:

    1. The default Router activity, isNewJob, checks if the task flow parameter jobId is null. If null, this flags the creation of a new record, or alternatively, a not null jobId displays that job.
    2. Figure 10: The "job-details-task-flow" contains a router to check if the instance is for record creation or just for viewing of an existing record.

      The job-details-task-flow contains a router to check if the instance is for record creation or just for viewing of an existing record.

    3. If the Router decides to create a new record, the Method Call, initialize, function creates a new record via the EJB/JPA records.
      
         
      public void initialize(){
       Job job =
       (Job)getBindings().getOperationBinding("findJobById").execute();
       //do some initialization
       job.setJobTitle("Default Job Title");
       //ensures that form is displayed in edit-mode
       getPageFlowScope().put(EDIT_MODE, true);
      }
      
      
    4. The final page fragment activity, job_details.jsff, is used to view, edit, or create a new Job. This fragment utilizes findById so that multiple record viewing is possible.

D. ADF Pages & Fragments

  1. The First.jspx page is simply the landing page for the based on the UI Shell and demonstrates the features supported by the Oracle Dynamic Tab Shell Template. This page was modified to display the Jobs link within the navigation facet section.
  2. The job_list.jsff page fragment shows the list of jobs in a table. This is launched as a new tab from the “Jobs” navigation link:
  3. 
       
    <af:commandToolbarButton text="New" id="ctb2" actionListener="#{backingBeanScope.jobListForm.create}"/>
    
    
  4. One fragment, job_details.jsff, supports the view, edit, and create record pages.

    Input components on this fragment are disabled thru the following expression in the readOnly property:

    
       
    <af:inputText value="#{bindings.jobTitle.inputValue}"
          label="#{bindings.jobTitle.hints.label}"
          required="#{bindings.jobTitle.hints.mandatory}"
          columns="#{bindings.jobTitle.hints.displayWidth}"
          maximumLength="#{bindings.jobTitle.hints.precision}"
          shortDesc="#{bindings.jobTitle.hints.tooltip}" id="it3"
          readOnly="#{!pageFlowScope.editMode}"
          autoSubmit="true">
    <f:validator binding="#{bindings.jobTitle.validator}"/>
    <f:valueChangeListener type="src.view.listeners.DirtyListener"/>
    </af:inputText>
    
    

E. ADF Managed Beans

  1. BaseForm.java is the super class of the backing beans. This hosts methods that can be used by the backing beans like launchActivity, getCurrentRowDataProvider, closeCurrentTab, setCurrentTabDirty, getMatchingTabIndex, and others.
  2. JobListForm.java is the backing bean of job_list.jsff. This contains action listeners that support the New and Edit buttons and the links on each record.
    
       
    public class JobListForm extends BaseForm {
    
     private static final String JOB_DETAILS_TASK_FLOW =
      "/WEB-INF/flows/job-details-task-flow.xml#job-details-task-flow";
    
     public void view(ActionEvent actionEvent) {
      launchJobDetails(false);
     }
    
     public void edit(ActionEvent actionEvent) {
      launchJobDetails(true);
     }
    
     public void create(ActionEvent event) {
      Map<String, Object> parameterMap = new HashMap<String, Object>();
      parameterMap.put("jobId", STRING_EMPTY_OBJECT_ID);
      String title = "Job: *";
      launchActivity(title, JOB_DETAILS_TASK_FLOW, parameterMap, true);
     }
    
     private void launchJobDetails(boolean editMode){
      Job job = (Job)getCurrentRowDataProvider("findAllJobsIterator");
      Map<String, Object> parameterMap = new HashMap<String, Object>();
      parameterMap.put("jobId", job.getJobId());
      parameterMap.put(IDENTIFIER, job.getJobId());
      if (editMode){
      parameterMap.put(EDIT_MODE, editMode);
      }
      String title = "Job: " + job.getJobId();
      launchActivity(title, JOB_DETAILS_TASK_FLOW, parameterMap, true);
     }
    }
    
    
  3. JobDetailsForm.java is the backing bean of job_details.jsff. This contains action listeners that support the Cancel, and Save and Close buttons. The action listener that supports the Edit button is inherited from the BaseForm.
    
       
    public class JobDetailsForm extends BaseForm {
    
     private static final String JOB_LIST_TASK_FLOW =
      "/WEB-INF/flows/job-list-task-flow.xml#job-list-task-flow";
    
     public void cancel(ActionEvent actionEvent) {
      setCurrentTabClean();
      closeCurrentTab();
      //tries to activate the Overview tab if present
      activateTab(JOB_LIST_TASK_FLOW);
     }
    
     public void saveAndClose(ActionEvent actionEvent) {
      save();
      setCurrentTabClean();
      closeCurrentTab();
      activateTab(JOB_LIST_TASK_FLOW);
     }
    
     private void save() {
      OperationBinding oper = null;
      if (isNewJob()) {
       oper = getOperationBinding("persistJob");
      } else {
       oper = getOperationBinding("mergeJob");
      }
      oper.execute();
     }
    
     private boolean isNewJob() {
      return STRING_EMPTY_OBJECT_ID.equals(getPageFlowScope().get("jobId"));
     }
     // initialize method
    
    }
    
    

Pattern Implementation

To recreate the demonstration used to illustrate the Create, Read and Update behavior within the UI Shell, follow the steps below. The implementation of this demonstration is divided into 7 major steps.

  1. Create a JPA Entity Model Object & an EJB Session Bean.
  2. Create backing beans for the action listeners that support actions like New, Edit, and View details.
  3. Create a page based on the ADF UI Shell template that provides a launch bean for the tabs.
  4. Create ADF task flows that will launch the job list on a separate tab and support the viewing, editing, and creation of records in additional tabs.
  5. Create fragments used within task flows the hosts the overview, create, edit, and view UI.

Create Workspace

To start the development of this demonstration application:

  1. From the JDeveloper IDE's toolbar select the New icon to create a new application workspace.
  2. In the New Gallery dialog with the Current Project Features tab selected, select the Application category under the General category, then the Java EE Web Application item from the right hand side.
  3. Click Ok.
  4. In the resulting Create Java EE Web Application wizard on the Name your application page of the wizard, type UIShell_EJB_CRU for the Application Name. Ensure there are no spaces in the name.
  5. For the Application Package Prefix, type src.
  6. Click Next
  7. In the Name field for the first project, type uiShellViewController for the ViewController project.
  8. Click Next.
  9. Click Next again to accept defaults for the Configure Java settings of the first project.
  10. For the Name your project options for the second project, type uiShell_EJB_Model for the project name. Note the EJB and Java default project features.
  11. Click Next.
  12. Click Next to accept defaults for Configure Java settings
  13. For the Configure EJB settings page of the wizard, in the Invoke Wizard drop down select Entities from Tables. Leave all other fields as default.
  14. Click Finish.

Create Entities from Tables: JPA Entity Model Object

To define the EJB/JPA objects for our Model project:

  1. In Persistence Unit page of the Entities from Tables wizard, click Next.
  2. In Type of Connection, accept Online Database Connection.
  3. Click Next.
  4. In Database Connection Details, create or copy a connection to the HR schema in the XE Database.
    1. Click Help in the Create Database Connection dialog to properly create a database connection.
  5. Click Next.
  6. For Select Tables page ensure Auto-Query is enabled.
  7. From the Available list, shuttle JOBS to the Selected list on the right hand side of the wizard page.
  8. Click Next.
  9. For the General Options page, accept the defaults and click Next.
  10. On the Specify Entity Details page change the Entity Name to Job without the "s".
  11. Click Next
  12. Click Finish.
  13. If you see a dialog stating that the Job.java class isn't available in the uiShellViewController project and should JDeveloper search for it elsewhere, click Yes.

Create a Predetermined Constant

Later on in the job-detail-task-flow.xml task flow, when creating a brand new Job record, pre-populate the Job record with the defined constant EMPTY for the JobId. This will allow the job-detail-task-flow to know if it's working with a new record or an existing record. This will change the operations the user can undertake on the record.

To create a predetermined constant:

  1. Right click the uiShell_EJB_Model project of the Application Navigator and select New.
  2. In the New Gallery, select Java within the top-most General category then the Java Package item from the resulting list on the right hand side.
  3. Click OK.
  4. For the package name, type src.constants.
  5. Click OK.
  6. Open a context menu on the src.constants package in the Application Navigator and select New.
  7. In the New Gallery, select Java Class under Java in the General categories.
  8. Click OK.
  9. Type Constants for the Name in the Create Java Class dialog.
  10. Deselect Constructors from Superclass.
  11. Deselect Implement Abstract Methods.
  12. Click OK.
  13. Within the resulting class add the following code:
    
    
    public static final String STRING_EMPTY_OBJECT_ID = "EMPTY";
    public static final Long LONG_EMPTY_OBJECT_ID = -1L;
    
    

Create EJB Session Bean

In order to allow the application to access the Job.java class that was created by the Entities from Tables wizard, we must provide an EJB session bean:

  1. Open a context menu on the uiShell_EJB_Model project in the Application Navigator and select New.
  2. In the New Gallery, select the EJB category and Session Bean item.
  3. Click Next
  4. In the resulting Create Session Bean wizard, in the General page for EJB Name type HRFacade. Tick the box that says Generate Session Façade Methods and select the uiShell_EJB_Model as the persistence unit.
  5. Click Next.
  6. For the Session Façade page of the wizard the Select JPA Entity Methods option should already be selected as well as all its children (the methods queryByRange, persistJob, mergeJob, removeJob and getJobFindAll).
  7. Click Next.
  8. For Class Definitions, update the Bean Class to src.service.HRFacadeBean.
  9. Click Next.
  10. For the Interfaces page, accept the defaults.
  11. Click Next.
  12. Click Finish.

Modify HRFacadeBean

The previous wizard creates the HRFacadeBean.java session bean and supporting artifacts. For the purposes of this application modifications are made to it.

In the resulting HRFacadeBean.java class:

  1. Add the following import.

    import static src.constants.Constants.STRING_EMPTY_OBJECT_ID;

  2. Add the following findJobById method to HRFacadeBean to return an empty Job object if the id argument that was passed into the method corresponds to a predetermined constant with value like EMPTY for String IDs. This will assist the job-details-task-flow that we create later on to distinguish between new Job records and existing Job records:
    
    
    public Job findJobById(String jobId) {
     if (jobId == null){
      throw new IllegalArgumentException();
     }
     if (STRING_EMPTY_OBJECT_ID.equals(jobId)){
      return new Job();
     }
     return em.find(Job.class, jobId);
    }
    
    
  3. Replace completely the existing getJobFindAll method in HRFacadeBean with the new method findAllJobs:
    
    
    /** select o from Job o */
    public List<Job> findAllJobs() {
     return em.createNamedQuery("findAllJobs").getResultList();
    }
    
    
  4. Save the application.

Modify HRFacade

In addition to the modifications made to HRFacadeBean, modifications to the the two interfaces that it is based on are needed.

Within the Application Navigator in the uiShell_EJB_Model project locate the HRFacade.java class under the src.service package:

  1. Add the following method to HRFacade.java:

    Job findJobById(String jobId);

  2. Replace the getJobFindAll method with the following in HRFacade.java:

    List<Job> findAllJobs();

  3. Save the application

Modify HRFacadeLocal

Now locate the HRFacadeLocal.java interface and do the same:

  1. Add the following method:

    Job findJobById(String jobId);

  2. Replace the getJobFindAll with the following:

    List<Job> findAllJobs();

  3. Save the application.

Modify Job

The getJobFindAll method with findAllJobs leaves a dangling reference in the Job.java class. Locate the Job.java class and the @NamedQueries annotation at top.

Expand @annotations:

  1. Modify the named query to the following.

    @NamedQueries( { @NamedQuery(name = "findAllJobs", query = "select o from Job o") })

  2. Change @Table(name = “JOBS") to @Table(name = “HR.JOBS")
  3. Save the application.

Create EJB Data Control

Now that we've created the basic Model layer for our application we need to expose this to the ViewController to access.

This is done through the provisioning of ADF Data Controls that make the data from the Model project available to the user interface in an ADF Fusion Web Application:

  1. Open a context menu on HRFacadeBean.java in the src.service package of the uiShell_EJB_Model project and select Create Data Control.
  2. In the resulting Choose EJB Interface dialog ensure the Local radio button is selected and the src.service.HRFacadeLocal option is selected in the relative dropdown.
  3. Click Ok.

Create Page Based on ADF UI Shell Template

We now have our basic Model business service for our application. From here we'll create the pages, page fragments and task flows of our application.

The first thing to create is our main landing page based on the ADF UI Shell. The best way to summarize the ADF UI Shell is it's a template with behaviours. The most salient behavior supported by the UI Shell is dynamic tabs. The tabs are dynamic in that they are rendered and dismissed upon demand by the user.

The task flows created later will display as regions within dynamic tabs within a page based on the ADF UI Shell template.

  1. Open a context menu on the uiShellViewController project.
  2. Select New.
  3. In the New Gallery, select the Web Tier category then the JSF / Facelets node, followed by the Page in the items list.
  4. Click OK.
  5. In the Create JSF Page dialog for the File Name, type First without any file type (e.g. .jspx or .jsf)
  6. Click on the Browse button to the right of the Directory field.
    1. It is a good practice to create a directory for each artifact type as was done in the Model project. This helps to distinguish similarly named artifacts that are of a different type.
  7. In the Choose Directory dialog, click the Folder icon in the upper right corner.
  8. In the Create New Directory dialog, type pages
  9. Click OK.
  10. Click Select with the new pages subdirectory selected
    1. Notice pages appended to the path shown in the Directory field and also ensure that the File Name you entered still does not have any file type suffix (e.g. .jspx or .jsf)
  11. Ensure that the Create as XML document checkbox is checked
  12. Select the Page Template radio button.
  13. Select Oracle Dynamic Tabs Shell in the drop down, if it is not selected by default.
    1. Note because you selected the Oracle Dynamic Tabs Shell for your application, and this is a relating ADF Faces technology, JDeveloper will now also configure your application to include the required supporting technologies including ADF Faces and ADF Task Flows.
  14. Click OK.
    1. You should now see the new page. In addition the Structure Window should also be open. If not select it via the View menu of the IDE.
  15. Click the Source tab at the bottom of the display view
  16. Click the Design tab at the bottom of the display
  17. In the Structure Pane (SP) (along the bottom left of the screen) select the af:document node. You may have to expand f:view to see it.
  18. For the Title field in the Common section of the Property Inspector, type Navigation Pane and Dynamic Tabs.

Add a Global Tab to the Page

Although this demonstration application has only one page, the ADF UI Shell supports multiple dynamic tabs. This allows multiple pages to present themselves as tab choices to the user. Once the ADF UI Shell page is created we'll later use its associated APIs to call backing beans to manage our tabs.

Steps to create a global tab :

  1. If not already open in the main editor of the IDE, within the uiShellViewController project locate and open the First.jspx in the Application Navigator.
  2. In the Structure Window, expand the tree until you reveal the globalTabs facet. Select it.
  3. In the Layout section of the Component Palette, select the Navigation Pane component to insert it into the globalTabs facet.
  4. In the Common section of the Component Palette, click the Navigation Item to insert it into the Navigation Pane.
  5. With the new Navigation Item selected in the Structure window, in the Common section of the Property Inspector, type Navigation Pane and Dynamic Tabs for the Text property.
  6. In the Behavior section of the Property Inspector, change the Selected property to true.
  7. Save the application.

As stated creating a page based on the ADF UI Shell Template was necessary to expose its APIs that are used by the backing beans created in the next section. The UI for this page is completed after the creation of the following backing beans.

Create Backing Beans

We'll now create four backing beans to serve as action listeners supporting actions like New, Edit, and View details, as well as accessing ADF UI Shell APIs for adding or selecting dynamic tabs and indicating to the user whether a task flow is clean or dirty. The code contained within these beans is mostly unexplained and will be left for you to investigate at your leisure.

Create Class BaseForm.java

Steps to create is a super class, BaseForm.java, containing methods that access the ADF UI Shell Template APIs inherited by other classes:

  1. Open a context menu on the uiShellViewController project.
  2. Select New.
  3. In the New Gallery select Java Class from the General category.
  4. Click Ok.
  5. For Name in the Create Java Class dialog, type BaseForm.
  6. For Package, type src.view.backing.
  7. Deselect Constructors from Superclass.
  8. Deselect Implement Abstract Methods.
  9. Click OK.
  10. In the resulting class file replace in entirely with the following code:
  11. 
    
    package src.view.backing;
    
    import java.util.List;
    import java.util.Map;
    import javax.faces.component.UIComponent;
    import javax.faces.event.ActionEvent;
    import oracle.adf.model.BindingContext;
    import oracle.adf.model.bean.DCDataRow;
    import oracle.adf.model.binding.DCIteratorBinding;
    import oracle.adf.view.rich.context.AdfFacesContext;
    import oracle.binding.BindingContainer;
    import oracle.binding.OperationBinding;
    import oracle.jbo.RowSetIterator;
    import oracle.ui.pattern.dynamicShell.Tab;
    import oracle.ui.pattern.dynamicShell.TabContext;
    
    public class BaseForm {
     public static final String EDIT_MODE = "editMode";
     public static final String IDENTIFIER = "identifier";
     protected void launchActivity(String title, String taskflowId,
      Map <String, Object> parameterMap,
      boolean newTab) {
     try {
      TabContext tabContext = getTabContext();
      if (newTab) { //allows multiple instance of taskflow.
       //if parameters contains identifier
       //try to select the taskflow with matching identifier
       Object identifier = parameterMap.get(IDENTIFIER);
       if (identifier != null) {
        int index = getMatchingTabIndex(taskflowId, identifier);
        if (index != -1) {
         activateTab(index);
         return;
        }
       }
       tabContext.addTab(title, taskflowId, parameterMap);
      } else {
        tabContext.addOrSelectTab(title, taskflowId, parameterMap);
      }
     } catch (TabContext.TabOverflowException toe) {
       // causes a dialog to be displayed to the user saying that there are
       // too many tabs open - the new tab will not be opened...
       toe.handleDefault();
     }
     }
    
    public void launchMenu(ActionEvent event) {
     UIComponent component = event.getComponent();
     String title = (String)component.getAttributes().get("title");
     String taskFlowId =
     (String)component.getAttributes().get("taskflowId");
     Map<String, Object> parameterMap =
     (Map<String, Object>)component.getAttributes().get("parameterMap");
     Boolean newTab = (Boolean)component.getAttributes().get("newTab");
     if (newTab == null) {
      newTab = false;
     }
     launchActivity(title, taskFlowId, parameterMap, newTab);
    }
    
    public Object getCurrentRowDataProvider(String iteratorName) {
     BindingContainer bindings = getBindings();
     DCIteratorBinding dcib = (DCIteratorBinding)bindings.get(iteratorName);
     RowSetIterator iter = dcib.getRowSetIterator();
     DCDataRow row = (DCDataRow)iter.getCurrentRow();
     return row.getDataProvider();
    }
    
    public BindingContainer getBindings() {
     return BindingContext.getCurrent().getCurrentBindingsEntry();
    }
    
    public void setCurrentTabDirty(boolean dirty) {
     getTabContext().markCurrentTabDirty(dirty);
    }
    
    public void setCurrentTabClean() {
     setCurrentTabDirty(false);
    }
    
    public void setCurrentTabDirty() {
     setCurrentTabDirty(true);
    }
    
    public Map<String, Object> getPageFlowScope() {
     return AdfFacesContext.getCurrentInstance().getPageFlowScope();
    }
    
    public void edit(ActionEvent event) {
      getPageFlowScope().put(EDIT_MODE, true);
    }
    
    protected OperationBinding getOperationBinding(String methodAction) {
     OperationBinding oper =
     getBindings().getOperationBinding(methodAction);
     if (oper == null) {
     throw new IllegalArgumentException(methodAction +
     " operation not found");
     }
     return oper;
    }
    
    public void closeCurrentTab(ActionEvent actionEvent) {
     closeCurrentTab();
    }
    
    public void closeCurrentTab() {
     getTabContext().removeCurrentTab();
    }
    
    public TabContext getTabContext() {
      return TabContext.getCurrentInstance();
    }
    
    protected void updateCurrentTabTitle(String title) {
      getCurrentTab().setTitle(title);
    }
    
    protected Tab getCurrentTab() {
     TabContext tabContext = getTabContext();
     return tabContext.getTabs().get(tabContext.getSelectedTabIndex());
    }
    
    public boolean isCurrentTabDirty() {
     return getTabContext().isCurrentTabDirty();
    }
    
    protected int getMatchingTabIndex(String taskflowId, Object identifier) {
     if (taskflowId == null || identifier == null) {
      return -1;
     }
     List<Tab> tabs = getTabContext().getTabs();
     for (int i = 0; i < tabs.size(); i++) {
     Tab tab = tabs.get(i);
     if (tab == null || !tab.isActive())
     continue;
     if (tab.getTaskflowId().getFullyQualifiedName().equals(taskflowId)) {
     Map<String, Object> parametersMap = tab.getParameters();
     if (identifier.equals(parametersMap.get(IDENTIFIER))) {
      return i;
     }
     }
     }
     return -1;
    }
    
    protected void activateTab(int index) {
     if (index == -1) {
      return;
     }
     getTabContext().setSelectedTabIndex(index);
    }
     //activates a tab with a specified taskflowID
    protected void activateTab(String taskflowId) {
     activateTab(getTabContext().getFirstTabIndex(taskflowId));
    }
    }
    
    

Ensure ViewController project has recognized dependency on the Model project

The uiShellViewController project has an obvious dependency on the Model project. However, it is not recognized by default.

To rectify this we will set project properties to map the dependency:

  1. Open a context menu on the uiShellViewController project.
  2. Select Project Properties
  3. In the Project Properties dialog select Dependencies.
  4. Click the Edit button in the Dependent Projects and Archives section.
  5. Expand the uiShell_EJB_Model.jpr node.
  6. Check the Build Output option.
  7. Click Ok.
  8. Click Ok again.
  9. Save your application.

Create Class JobListForm.java

The second class to create JobListForm.java will be the backing bean of the page fragment job_list.jsff created later. It inherits, among other things, a dynamic tab launch method from BaseForm.java while providing action listeners that support the New button and a view link.

  1. Open a context menu on the uiShellViewController project.
  2. Select New
  3. In the New Gallery select Java Class from the General category.
  4. Click Ok.
  5. For Name in the Create Java Class dialog, type JobListForm.
  6. For Package, type src.view.backing.
  7. Deselect Constructors from Superclass.
  8. Deselect Implement Abstract Methods.
  9. Click OK.
  10. Override the resulting class with the following source code:
  11. 
    
    package src.view.backing;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.faces.event.ActionEvent;
    import static src.constants.Constants.STRING_EMPTY_OBJECT_ID;
    import src.Job; // If this doesn’t work try src.model.Job instead
    
    public class JobListForm extends BaseForm {
    
     private static final String JOB_DETAILS_TASK_FLOW = "/WEB-INF/Taskflows/job-details-task-flow.xml#job-details-task-flow";
     public void view(ActionEvent actionEvent) {
      launchJobDetails(false);
     }
    
     public void edit(ActionEvent actionEvent) {
      launchJobDetails(true);
     }
    
     public void create(ActionEvent event) {
      Map<String, Object> parameterMap = new HashMap<String, Object>();
      parameterMap.put("jobId", STRING_EMPTY_OBJECT_ID);
      String title = "Job: *";
      launchActivity(title, JOB_DETAILS_TASK_FLOW, parameterMap, true);
     }
    
     private void launchJobDetails(boolean editMode){
      Job job = (Job)getCurrentRowDataProvider("findAllJobsIterator");
      Map<String, Object> parameterMap = new HashMap<String, Object>();
      parameterMap.put("jobId", job.getJobId());
      parameterMap.put(IDENTIFIER, job.getJobId());
      if (editMode){
       parameterMap.put(EDIT_MODE, editMode);
      }
      String title = "Job: " + job.getJobId();
      launchActivity(title, JOB_DETAILS_TASK_FLOW, parameterMap, true);
     }
    }
    
    

Create Class JobDetailsForm.java

The third class to create JobDetailsForm.java is the backing bean of page fragment, job_details.jsff, which we will create later.

This contains action listeners that support the Cancel, Save and Close buttons. The action listener that supports the Edit button is inherited from the BaseForm.

  1. Open a context menu on the uiShellViewController project.
  2. Select New.
  3. In the New Gallery select Java Class from the General category.
  4. Click Ok.
  5. For Name in the Create Java Class dialog, type JobDetailsForm.
  6. For Package, type src.view.backing.
  7. Deselect Constructors from Superclass.
  8. Deselect Implement Abstract Methods.
  9. Click OK.
  10. Replace the resulting Java code with the following:
  11. 
        
    package src.view.backing;
    
    import javax.faces.event.ActionEvent;
    import oracle.binding.OperationBinding;
    import static src.constants.Constants.STRING_EMPTY_OBJECT_ID;
    import src.Job; // If this doesn’t work try src.model.Job instead
    public class JobDetailsForm extends BaseForm {
    
     private static final String JOB_LIST_TASK_FLOW =
     "/WEB-INF/Taskflows/job-list-task-flow.xml#job-list-task-flow";
    
     public void cancel(ActionEvent actionEvent) {
      setCurrentTabClean();
      closeCurrentTab();
      //tries to activate the Overview tab if present
      activateTab(JOB_LIST_TASK_FLOW);
     }
    
     public void saveAndClose(ActionEvent actionEvent) {
      save();
      setCurrentTabClean();
      closeCurrentTab();
      activateTab(JOB_LIST_TASK_FLOW);
     }
    
     private void save() {
      OperationBinding oper = null;
      if (isNewJob()) {
       oper = getOperationBinding("persistJob");
      } else {
      oper = getOperationBinding("mergeJob");
      }
      oper.execute();
     }
    
     private boolean isNewJob() {
      return STRING_EMPTY_OBJECT_ID.equals(getPageFlowScope().get("jobId"));
     }
    
     public void initialize() {
      Job job =
      (Job)getBindings().getOperationBinding("findJobById").execute();
      //do some initialization
      job.setJobTitle("Default Job Title");
      //ensures that form is displayed in edit-mode
      getPageFlowScope().put(EDIT_MODE, true);
     }
    }
    
    

Create Class DirtyListener.java

The fourth class to create, DirtyListener.java, is the backing bean of page fragment job_details.jsff. It is a custom class that implements the ValueChangeListener interface and calls the markCurrentTabDirty ADF UI Shell API. When the value of a UI component associated with the DirtyListener class changes, the text title of the dynamic tab of the ADF UI Shell will turn to italics. This is a signal to the user that the current tab now has dirty/modified data.

This is useful feedback when there are potentially several open tabs in the UI.

  1. Open a context menu on the uiShellViewController project.
  2. Select New.
  3. In the New Gallery select Java Class from the General category.
  4. Click Ok.
  5. For Name in the Create Java Class dialog, type DirtyListener.
  6. For Package, type src.view.listeners.
  7. Deselect Constructors from Superclass.
  8. Deselect Implement Abstract Methods.
  9. Click OK.
  10. Replace the code with the following:
  11. 
    
    package src.view.listeners;
    
    import javax.faces.event.AbortProcessingException;
    import javax.faces.event.ValueChangeEvent;
    import javax.faces.event.ValueChangeListener;
    
    import oracle.ui.pattern.dynamicShell.TabContext;
    
    public class DirtyListener implements ValueChangeListener {
    
     public void processValueChange(ValueChangeEvent valueChangeEvent) throws AbortProcessingException {
      TabContext tabContext = TabContext.getCurrentInstance();
      if (tabContext != null) {
       tabContext.markCurrentTabDirty(true);
      }
     }
    }
    
    
  12. Save the application.

Create ADF Taskflows

The demonstration application is composed of two task flows; One which is launched for the overview tab and the other, which supports the viewing, editing, and creation of records in additional tabs. These task flows are called via an API provided by the ADF UI Shell Template invoked via our beans.

Create Taskflow: Job List

  1. Open a context menu on uiShellViewController.
  2. Select New.
  3. In the New Gallery under Web Tier, select the JSF / Facelets category. (You may have to select the View All Technologies tab)
  4. On the right hand side, select the ADF Task Flow.
  5. In the Create Task Flow dialog, type job-list-task-flow for the task flow name.
  6. Click on the Browse button to the right of the Directory field.
  7. In the Choose Directory dialog, click the Folder icon in the upper right corner.
  8. In the Create New Directory dialog, type Taskflows. Note the case of this directory is important, and ensure you add no spaces.
  9. Click OK.
  10. Click Select.
    1. Notice Taskflows appended to the path shown in the Directory field.
  11. Click OK.
    1. The diagram for the taskflow appears as an editor pane.

Register JobListForm Bean

UI components are components of page or page fragments which are accessed via view activities in task flows. Such UI components make use of the backing beans that we created earlier to provide values and behavior. To bind UI components to the created backing beans we must first register that bean to the owning task flow.

Thereafter we can reference the backing bean as a property value of a UI component through an EL expression:

  1. At the base of the editor pane for the job-list-task-flow, select Overview.
  2. In the Overview, select the Managed Beans tab.
    1. A managed bean is a registered backing bean.
  3. Click [+] (Add) once to open an entry in the Managed Beans table.
  4. In the table type jobListForm for the Name.
  5. In the next column, type or select src.view.backing.JobListForm for the Class.
  6. In the next column, select backingBean for the Scope.
  7. Select the Parameters tab
  8. Click [+] (Add) once to open an entry in the Input Parameter Definitions table.
  9. In the table type tabContext for the Name.
  10. In the next column, type or select oracle.ui.pattern.dynamicShell.TabContext for the Class.
  11. In the next column, type or select #{pageFlowScope.tabContext}
  12. In the next column, enable the Required check box.
  13. Save your application

Design Page Fragment: Overview Tab

  1. At the base of the editor pane for the job-list-task-flow, select Diagram.
  2. Drag and drop a view activity onto the task flow diagram from the Component Palette
  3. Change the name to job_list.
  4. Open the job_list view activity by double clicking it.
    1. This should result in the Create ADF Page Fragments dialog with the file name defaulted to job_list.jsff.
  5. Click on the Browse button to the right of the Directory field.
  6. Choose Directory dialog, click the Folder icon in the upper right corner.
  7. In the Create New Directory dialog, type fragments.
  8. Click OK.
  9. Click Select.
    1. Notice “fragments” appended to the path shown in the Directory field.
  10. Click OK.
    1. Note in the Structure Window only one component is showing: jsp.root and it should be selected by default.
  11. In the Layout section of the Component Palette, select the Panel Header component to insert it into the page fragment.
  12. In the Common section of the Property Inspector, change the Text value to Manage Jobs.
  13. With the Panel Header – Manage Jobs component selected in the Structure Window, click on the Panel Header option again in the Component Palette.
    1. This results in a nested Panel Header.
  14. In the Common section of the Property Inspector, change the Text value to Jobs.
  15. While Panel Header – Jobs remains selected in the Structure Window, click on Panel Collection in the Component Palette.
    1. The result is Panel Collection nested with Panel Header – Jobs.
  16. In the Behavior section of the Property Inspector, type detach for the FeaturesOff value.
    1. This optional step hides the detach feature from the Panel Collection toolbar.
  17. In the Data Control panel of the Application Navigator expand the HRFacadeLocal data control
  18. From the Data Control panel locate the findAllJobs data control type, expand it and drag and drop the Job option onto the Panel Collection in the Structure Window (in design view).
  19. From the Create popup that displays, select ADF Read Only Table.
  20. In the Edit Table Columns dialog, select Single Row.
  21. Click OK.
    1. The result is a read only table added to the page fragment.
  22. In the Structure Window locate the table component then select the jobId af:column.
  23. In the Component Palette select Link.
  24. With the new commandLink selected in the Structure Window change the text property in the Property Inspector to #{row.jobId}
  25. For the same commandLink change the actionListener property in the Property Inspector to #{backingBeanScope.jobListForm.view}
  26. Within the same jobId af:column you'll note an af:outputText. Select and delete this.
  27. From the Menus and Toolbars section of the Component Palette, drag and drop the Toolbar into the toolbar facet of the Panel Collection in the editor.
  28. From the Menus and Toolbars section of the Component Palette, drag and drop a Toolbar Button into the toolbar in the Panel Collection you just created.
  29. In the Common section of the Property Inspector, change the Text value to New.
  30. In the Common section of the Property Inspector, click Edit from the property menu of the ActionListener property.
  31. In the Edit Property: ActionListener dialog, select jobListForm in the Managed Bean dropdown.
  32. In the Method dropdown select the create option.
  33. Click OK.
  34. In the Structure Window, select the Toolbar component.
  35. From the Menus and Layouts section of the Component Palette, click once on the Toolbar Button.
  36. In the Common section of the Property Inspector, change the Text value to Edit.
  37. In the Common section of the Property Inspector, click Edit from the property menu of the ActionListener property.
  38. In the Edit Property: ActionListener dialog, select jobListform in the Managed Bean dropdown.
  39. Select the first instance of edit in the Method dropdown.
  40. Click OK.
  41. In the Data Control panel expand the findAllJobs data control type.
  42. Expand the child Operations node.
  43. Drag and drop the Execute operation on to the Toolbar component of the Structure Window
  44. In the Create popup, select the ADF Toolbar Button.
    1. The toolbar button should appear to the right of the Edit button.
  45. In the Common section of the Property Inspector, change the Text value to Refresh.
  46. In the Behavior section of the Property Inspector, reset the Disabled property to default by selecting the option from the right most edit button drop down icon.
  47. Save the application.

Create Taskflow: Job Details

  1. Open a context menu on the Taskflows node within the Application Navigator
  2. Select New.
  3. In the New Gallery under the Web Tier node, select the JSF category.
  4. On the right hand side, select the ADF Task Flow.
  5. In the Create Task Flow dialog, type job-details-task-flow for the task flow name
    1. Notice “Taskflows” has been appended to the path name in Directory
  6. Click OK.
  7. Save the application.

Register JobDetailsForm Bean

  1. At the base of the editor pane for the job-details-task-flow, select Overview.
  2. In the Overview, select the Managed Beans tab.
    1. A managed bean is a registered backing bean.
  3. Click [+] (Add) to open an entry in the Managed Beans table.
  4. In the table type jobDetailsForm for the Name.
  5. In the next column, type or select src.view.backing.JobDetailsForm for the Class.
  6. In the next column, select backingBean for the Scope.
  7. Select the Parameters tab
  8. Click [+](Add) to open an entry in the Input Parameter Definitions table.
  9. In the table type tabContext for the Name.
  10. In the next column, type or select oracle.ui.pattern.dynamicShell.TabContext for the Class.
  11. In the next column, type or select #{pageFlowScope.tabContext}
  12. In the next column, enable Required.
  13. Click [+] (Add) to open another entry in the Input Parameter Definitions table.
  14. In the table type jobId for the Name. Note the case of the name is important.
  15. In the next column, type or select java.lang.String for the Class.
  16. In the next column, type or select #{pageFlowScope.jobId}
  17. In the next column, enable Required.
  18. Click [+] (Add) to open another entry in the Input Parameter Definitions table.
  19. In the table type editMode for the Name.
  20. In the next column, type or select java.lang.Boolean for the Class.
  21. In the next column, type or select #{pageFlowScope.editMode}
  22. Select the Behavior tab.
  23. Under Transactions, make sure the Share data controls with calling task flow check box is unchecked. If it shows a blue square or dash this is wrong. It must be completely unselected, or in other words the checkbox must be empty.

Diagram of JobDetailsForm Taskflow

  1. At the base of the editor pane for the job-details-task-flow, select Diagram.
  2. From the Component Palette drag and drop the Router component.
  3. Type isNewJob for the name.
  4. From the Component Palette drag and drop the Method Call component.
  5. Type initialize for the name.
  6. From the Component Palette drag and drop the View component.
  7. Type job_details for the name.
  8. Drag and drop a Control Flow Case from the Component Palette onto the Router and drag it to the initialize Method Call.
  9. Type initialize for the name of the Control Flow Case.
  10. Drag and drop another Control Flow Case from the Component Palette onto the Method Call and drag it to the job_details View.
  11. Type toEdit for the name of this Control Flow Case.
  12. Drag and drop another Control Flow Case from the Component Palette onto the Router and drag it to the job_details view.
  13. Type toEdit for the name.
  14. Select the Router in the diagram.
  15. In the General section of the Property Inspector, select toEdit in the Default Outcome drop down.
  16. Click [+] (Add) for Cases in the Property Inspector.
  17. Type #{pageFlowScope.jobId eq 'EMPTY'}for Expression.
  18. Select initialize in the Outcome drop down.
  19. Select the Method Call in the diagram.
  20. In the General section of the Property Inspector, type #{backingBeanScope.jobDetailsForm.initialize} for the Method property.
  21. Type toEdit in the Fixed Outcome drop down.
  22. Still in the task flow diagram right click the Method Call and select Create Page Definitions.
  23. Click [+] (Add) button next to the Bindings section.
  24. In the ensuing Insert Item dialog select methodAction.
  25. Click Ok.
  26. In the Create Action Binding dialog select the HRFacadeLocal data collection option.
  27. In the Operation drop down select findJobById
  28. In the Value of the parameter jobId enter #{pageFlowScope.jobId} Click Ok.
  29. Save your application.

Design Page Fragment: Job Detail Tab

  1. Open the job_details view activity by double clicking it in the task flow diagram
    1. This should result in the Create New JSF Page Fragment dialog with the file name defaulted to job_detail.jsff.
  2. Click on the Browse button to the right of the Directory field.
  3. In the Choose Directory dialog tree, select fragments
  4. Click Select.
    1. Notice fragments appended to the path shown in the Directory field.
  5. Click OK.
  6. In the Layout section of the Component Palette, select the Panel Header component to insert it into the page fragment.
  7. In the Common section of the Property Inspector, change the Text value to #{pageFlowScope.editMode? 'Edit ':'View '}Job: #{bindings.jobId}
  8. While Panel Header remains selected in the Structure Window, click on Panel Header in the Component Palette.
    1. This results in a nested Panel Header.
  9. In the Common section of the Property Inspector, change the Text value to Basic Information.
  10. From the Data Controls palette in the Application Navigator locate the findJobById node and the Job child node, and drag and drop the Job node onto the Basic Information Panel Header.
  11. In the Create popup, select ADF Form
  12. In the resulting Edit Forms Fields dialog select Ok.
  13. Select the jobId af:inputText box in the Structure window
  14. In the Structure Window select the jobId inputText control.
  15. For the Value of the parameter jobId set the Value to #{bindings.jobId.inputValue}.
  16. In the Property Inspector under the Behaviour node override the ReadOnly property with the following EL expression (open the expression builder): #{!pageFlowScope.editMode or bindings.findJobByIdIterator.currentRow.dataProvider.jobId ne null}
  17. The bindings.findJobByIdIterator.currentRow.dataProvider.jobId line may be underlined with red. Ignore this for now.
  18. In the Structure Window select the jobTitle, minSalary and maxSalary fields at the same time (ensure they're all selected).
  19. In the Property Inspector under the Behaviour node, override the ReadOnly property with the following EL expression: #{!pageFlowScope.editMode}
  20. Still in the Structure Window select all four fields, the jobId, jobTitle, minSalary and maxSalary.
  21. In the Property Inspector under the Behaviour node, set autoSubmit = true.
  22. In the Structure Window right click the jobId, select Insert Inside af:inputText, JSF and Value Change Listener.
  23. Repeat the last step for the other three field jobTitle, minSalary and maxSalary.
  24. Select each of the f:valueChangeListeners you just created for the four inputText components, and within the Property Inspector for each, change the type property to: src.view.listeners.DirtyListener
  25. Within the Structure Window select the parent Panel Header.
  26. Within the Structure Window still, locate the toolbar facet of the parent Panel Header.
  27. From the Component Palette drag in a Toolbar into the toolbar facet
  28. With the new Toolbar component selected in the Structure Window, drag four (4) Toolbar Buttons into the toolbar.
  29. Select the first Command Toolbar Button in the Structure Window.
  30. Via the Property Inspector change the text property to Edit.
  31. Again via the Property Inspector change the Rendered property to #{not pageFlowScope.editMode}
  32. Now in the Property Inspector dialog enter #{backingBeanScope.jobDetailsForm.edit} in the actionListener property.
  33. Select the second Command Toolbar Button in the Structure Window
  34. Via the Property Inspector change the text property to Cancel.
  35. Again via the Property Inspector change the rendered property to #{not pageFlowScope.editMode}
  36. Yet again in the Property Inspector dialog type #{backingBeanScope.jobDetailsForm.cancel} in the actionListener property.
  37. Select the third Command Toolbar Button in the Structure Window
  38. Via the Property Inspector change the text property to Save and Close.
  39. Again via the Property Inspector change the rendered property to #{pageFlowScope.editMode}
  40. Yet again in the Property Inspector dialog type #{backingBeanScope.jobDetailsForm.saveAndClose} in the actionListener property.
  41. Select the fourth and final Command Toolbar Button in the Structure Window
  42. Via the Property Inspector change the text property to Cancel.
  43. Again via the Property Inspector change the rendered property to #{pageFlowScope.editMode}
  44. Second last time, in the Property Inspector type #{backingBeanScope.jobDetailsForm.cancel} for the actionListener.
  45. And finally in the Property Inspector for the fourth button only, set it's Immediate property to true.
  46. In the Structure Window select the panelForm that wraps the four inputText controls.
  47. In the Property Inspector enter the id of the first Command Toolbar Button you created into the Partial Triggers property.
  48. Save your application
  49. With the jobs_details.jsff page fragment still open select the Bindings tab at the bottom of the editor.
  50. Click [+] (Add) button next to the Bindings section.
  51. In the resulting Insert Item dialog select methodAction.
  52. Click Ok.
  53. In the Create Action Binding dialog select the HRFacadeLocal data collection option.
  54. In the Operation drop down select persistJob
  55. In the Value of the parameter job type #{bindings.findJobByIdIterator.currentRow.dataProvider}
  56. Select Ok.
  57. Still with the bindings select the [+](Add) button once again and select a methodAction.
  58. In the Create Action Binding dialog select the HRFacadeLocal data collection option.
  59. In the Operation drop down select mergeJob
  60. In the Value of the parameter job type #{bindings.findJobByIdIterator.currentRow.dataProvider}
  61. Select Ok.

Completing the First page

At this point of time we have all the artefacts in place for our application. To complete it we simply need to add the ability to call the Overview tab (job-list-task-flow) from the First.jspx page:

  1. Open the First.jspx page via the Application Navigator.
  2. In the Structure Window locate the navigation facet of the Oracle Dynamic Tabs Shell page template and select it
  3. Via the Component Palette select a Panel Header.
  4. With the Panel Header selected in the Structure Window, within the Property Inspector update the Panel Header's text property to Navigation.
  5. Still with the Panel Header selected in the Structure Window, select another Panel Header from the Component Palette.
  6. Now set the new Panel Header's text property to Menu.
  7. With the 2nd Panel Header selected in the Structure Window, select a Panel List from the Component Palette.
  8. With the Panel List selected in the Structure Window select a Link from the Component Palette.
  9. Via the Property Inspector change the Command Link's text property to Jobs.
  10. Also in the Property Inspector change the Command Link's actionListener property to #{backingBeanScope.launcher.launchMenu}
  11. Right click on the Command Link and select Insert Inside Link followed by JSF Core then Attribute.
  12. In the ensuing Insert Attribute menu set the name property to title and the value property to Overview.
  13. Repeat step 11 to create another Attribute.Set the new attribute’s name property to taskflowId and value property to /WEB-INF/Taskflows/job-list-task-flow.xml#job-list-task-flow.
  14. Save your application.

You'll note in the previous steps a call to a managed bean named "launcher". We must configure this in the application's adfc-config.xml file.

  1. In the Application Navigator locate the uiShellViewController project's adfc-config.xml file and open it.
  2. Drag First.jspx from the Application Navigator onto the display area. This will cause your page to open when you run the application
  3. Select the Overview tab at the bottom of the adfc-config.xml file.
  4. Select the Managed Beans tab.
  5. Click [+] (Add) once to open an entry in the Managed Beans table.
  6. In the table type launcher for the Name.
  7. In the next column, type or select src.view.backing.BaseForm for the Class.
  8. In the next column, select backingBean for the Scope.
  9. inally, returning to the First.jspx page, locate the Document component and its Meta Container facet. If it shows a greyed-out folder, then right click it and select Facets – Document then Meta Container to create it
  10. In the First.jspx page editor select the Source tab at bottom of the editor page.

    Replace the Meta Container facet code as follows:

    
    
    <f:facet name="metaContainer">              
    <af:group id="g1">
    <af:resource type="javascript">                   
    function setCurrentTabDirty(event) {                    
    var source = AdfPage.PAGE.findComponent("d1");
    AdfCustomEvent.queue(source, "setCurrentTabDirty", {},false);                 
     }               
    </af:resource>                    
    </af:group>                 
    </f:facet>
    
    
  11. Save your applicaton.
  12. Run the Application.

Instead of completing these step, an ADF application on OTN called ADF_UIShell_EJB_CRU is available at [LINK]. It can be unzipped into the designated work area and opened with JDeveloper. This pattern has been tested against JDeveloper Studio 11.1.1.6.0.

Related Documentation

Documentation Related to the Pattern Artifacts
Oracle Dynamic Tabs Template Dynamic Tabs UI Shell Template Functional UI Pattern in Oracle ADF Functional Patterns
Enable/Disable an ADF Component Enable/Disable an ADF Component Functional UI Pattern in Oracle ADF Functional Patterns
ADF Task Flows Working with Task Flow Activities in Oracle ADF Functional Patterns
Contextual Events Creating Contextual Events in Fusion Developer’s Guide
ADF Model (JSR-227) Using ADF Model Data Binding in a Java EE Web Application in the Java EE Developer's Guide
EJB/JPA Getting Started with an EJB Business Services Layer in JDeveloper Online Help