Monday, May 28, 2012

Commit the child taskflow transaction in parent taskflow transaction using DataContolFrame Api

In this post I'm sharing sample for how can we use "DataContolFrame" to commit the child taskflow transaction in parent taskflow transaction. 

Take a scenario, where we have two taskflows. The parent taskflow contain employee table and child taskflow contains selected employee rows. Alter the values and save, once clicking on Save button will save the data into cache. Now in parent taskflow user can click on Commit button would commit all the changes down in the child taskflow to DB, Rollback button will undo all changes done in child taskflow.

Here DataControlFrame api is used to commit/rollback the data at taskflow controller level. A Data Control Frame is the container associated with a task flow that contains data control instances. To specify whether data control instances are shared between the calling and called task flows, you must set a data-control-scope value of either shared or isolated on the called bounded task flow. In above scenario we can use shared data-control-scope. 

[Runs with Oracle JDeveloper 11.1.2.0.0 (11g R2) + HR Schema]

Implementation Steps

Create Fusion Web Application with business components from tables based on Departments, Employees table, create a new extended object from Employees View as shown in below image.


Open the AppModule and select Data Model tab move "childEmployeesView" from available view objects to data model.


Next go to Java tab in AppModule and click on edit java options. Generate the application module class: AppModuleImpl.java.

Open the AppModuleImpl.java file and the below methods code.
/**
 * This method create's the view crietria at the runtime,
 * set the IN clause operator by accessing the ChildEmployeesView1
 * @param empList
 */
public void populateSelectedRowData(List empList) {
 try {
  if (empList.size() > 0) {
   //cleanUpChildEmployeesView1 just in case before populating new set of rows
   cleanUpChildEmployeesView1();
   ViewObject childEmpVo = getChildEmployeesView1();
   ViewCriteria childEmpVC = childEmpVo.createViewCriteria();
   ViewCriteriaRow childEmpVCRow = childEmpVC.createViewCriteriaRow();
   ViewCriteriaItem childEmpVCRowItem = childEmpVCRow.ensureCriteriaItem("EmployeeId");
   childEmpVCRowItem.setOperator("IN");
   for (int i = 0; i < empList.size(); i++) {
    childEmpVCRowItem.setValue(i, empList.get(i));
   }
   childEmpVC.addElement(childEmpVCRow);
   childEmpVo.applyViewCriteria(childEmpVC);
   childEmpVo.executeQuery();
   System.out.println("populateSelectedRowData - Estimated Row Count - " +
          childEmpVo.getEstimatedRowCount());
  }
 } catch (Exception e) {
  e.printStackTrace();
 }
}

/**
 * Clean up the childemployeesView existing rows from collection.
 */
public void cleanUpChildEmployeesView1() {
 ViewObject childEmpVO = (ViewObject)this.getChildEmployeesView1();
 //avoid validation when navigating rows
 childEmpVO.setRowValidation(false);
 while (childEmpVO.hasNext()) {
  childEmpVO.setCurrentRow(childEmpVO.next());
  childEmpVO.removeCurrentRowFromCollection();
 }
}

/**
 * This method will update the departmentId for selected Employees rows
 * @param empKeysList
 * @param departmentId
 */
public void updateAllChildEmployeesRows(List empKeysList, String departmentId) {
 if (empKeysList != null && empKeysList.size() > 0) {
  ViewObject empVO = (ViewObject)this.getEmployeesView1();
  Iterator iter = empKeysList.iterator();
  Key rowKey = null;
  Row[] matchingInvoiceRows = null;
  Row currRow = null;
  while (iter.hasNext()) {
   //get the key for the invoice row
   rowKey = (Key)iter.next();
   //find the row by key in InvoiceVO
   matchingInvoiceRows = empVO.findByKey(rowKey, 1);
   if (matchingInvoiceRows != null && matchingInvoiceRows.length > 0)
    currRow = matchingInvoiceRows[0];
   if (currRow != null) {
    if (departmentId != null) {
     currRow.setAttribute("DepartmentId", departmentId);
    }
   }
  }
 }
}

Go back to AppModule.xml and select Java tab in client interface section move populateSelectedRows, updateAllChildEmployeesRows from available to selected block.


In ViewController project, create below mentioned bounded taskflows.
  1. "EmployeeDetails-btf" and set the Transaction as "Always Begin New Transaction", select the data-control-scope "Shared data controls with calling task flow".
  2. "EmployeeEdit-btf" and set the Transaction as "Always Use Existing Transaction", select the data-control-scope "Shared data controls with calling task flow" and create two pageFlowScope parameters as empList and empKeyList.
Open EmployeeDetail-btf, from component palette drop view as "EmployeeDetails" and generate EmployeeDetails.jsff page and "EmployeeDetailsBean.java". Add the EmployeeDetailsBean.java file in taskflow with scope requestScope.
  • From data control palette drag and drop EmployeesView1->Table as ADF Read Only Table, set RowSelection as "muliptle". 
  • Surround the table with panel collection component and add Edit button with text as "Edit". Create the actionListener method for edit button as "onEditAction".
  • Add two more button to the page Commit/Rollback and create actionListener method as "onCommit","onRollback" respectively.
  • From component palette drop popup inside the page and drop "EmployeeEdit-btf" as region inside popup, set the popupCanceledListener, popupFetchListener as show in the below code.
 <af:popup childCreation="deferred" autoCancel="disabled" id="p1" contentDelivery="lazyUncached"
              popupCanceledListener="#{EmployeeDetailsBean.popupCancelled}"
              popupFetchListener="#{EmployeeDetailsBean.poupFetchListener}" binding="#{EmployeeDetailsBean.popup}">
        <af:panelWindow id="pw1" title="Edit Employees">
            <af:region value="#{bindings.EmployeeEditbtf1.regionModel}" id="r1"
                       regionNavigationListener="#{EmployeeDetailsBean.hearNavigation}"/>
        </af:panelWindow>
  </af:popup>

Open the EmployeeDetailsBean.java file and copy the below methods code.
private RichPopup popup;
private RichTable empTable;

public EmployeeDetailsBean() {
}

public void setPopup(RichPopup popup) {
 this.popup = popup;
}

public RichPopup getPopup() {
 return popup;
}

public void setEmpTable(RichTable empTable) {
 this.empTable = empTable;
}

public RichTable getEmpTable() {
 return empTable;
}

public void popupCancelled(PopupCanceledEvent popupCanceledEvent) {
 ADFContext.getCurrent().getPageFlowScope().put("forceActivate", "false");
 System.out.println("popupCancelled");
}

public void poupFetchListener(PopupFetchEvent popupFetchEvent) {
 ADFContext.getCurrent().getPageFlowScope().put("forceActivate", "true");
 System.out.println("poupFetchListener");
}

public void hearNavigation(RegionNavigationEvent regionNavigationEvent) {
 this.popup.hide();

 AdfFacesContext.getCurrentInstance().addPartialTarget(this.empTable);
 System.out.println("Finished closing popup");
}

public void onEditAction(ActionEvent actionEvent) {
 RowKeySet selectedEmps = getEmpTable().getSelectedRowKeys();
 if (selectedEmps.size() > 0) {
  //Restart the taskflow
  DCBindingContainer dc = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
  DCTaskFlowBinding tf = (DCTaskFlowBinding)dc.findExecutableBinding("EmployeeEditbtf1");
  tf.getRegionModel().refresh(FacesContext.getCurrentInstance());

  Iterator selectedEmpIter = selectedEmps.iterator();
  DCBindingContainer bindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
  DCIteratorBinding empIter = bindings.findIteratorBinding("EmployeesView1Iterator");
  RowSetIterator empRSIter = empIter.getRowSetIterator();
  List selectedRowKeys = new ArrayList();
  List SelectedDeptIdRowValue = new ArrayList();
  while (selectedEmpIter.hasNext()) {
   Key key = (Key)((List)selectedEmpIter.next()).get(0);
   selectedRowKeys.add(key);
   Row currentRow = empRSIter.getRow(key);
   SelectedDeptIdRowValue.add(currentRow.getAttribute("EmployeeId"));
  }

  ADFContext.getCurrent().getRequestScope().put("empList", SelectedDeptIdRowValue);
  ADFContext.getCurrent().getRequestScope().put("empKeysList", selectedRowKeys);
  RichPopup.PopupHints hints = new RichPopup.PopupHints();
  this.popup.show(hints);
 }
}

public void onCommit(ActionEvent actionEvent) {
 Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
 BindingContext context = (BindingContext)sessionMap.get(BindingContext.CONTEXT_ID);
 String currentFrameName = context.getCurrentDataControlFrame();
 DataControlFrame dcFrame = context.findDataControlFrame(currentFrameName);
 dcFrame.commit();
 dcFrame.beginTransaction(null);
}

public void onRollBack(ActionEvent actionEvent) {
 Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
 BindingContext context = (BindingContext)sessionMap.get(BindingContext.CONTEXT_ID);
 String currentFrameName = context.getCurrentDataControlFrame();
 DataControlFrame dcFrame = context.findDataControlFrame(currentFrameName);
 dcFrame.rollback();
 dcFrame.beginTransaction(null);
 AdfFacesContext.getCurrentInstance().addPartialTarget(this.empTable);
}

Now go to EmployeeDetails.jsff page binding and set the condition for taskflow-EmployeeEditbtf1 executables as shown in below image.



Open the EmployeeEdit-btf, create the flow as shown below.



  • Drag and drop populateSelectedRowData from data control and set the parameter value for empList as "#{pageFlowScope.empList}".
  • From component palette drop view as "EmployeeEdit" and generate EmployeeEdit.jsff page and "EmployeeEditBean.java". Add the EmployeeEditBean.java file in taskflow with scope backingBean.
  • Draw the control flow case from  populateSelectedRowData to EmployeeEdit and keep default outcome
  • Drop  EmployeesView1->Execute from data control palette.
  • Draw the control flow case from EmployeeEdit to Execute and outcome as save.
  • Now add two Task Flow Return and name them as Save and Cancel.
  • Draw the control flow case from EmployeeEdit to Cancel and outcome as exit.
  • Draw the control flow case from Execute to Save and outcome as Execute.
Open EmployeeEdit.jsff page.
  • From data control palette drop ChildEmployeesView1->Table as ADF Table.
  • Drop DepartmentView1->Single Selection as ADF Select One Choice and select display attribute as departmentId and create valueChangeListener method as "getSelectedDeptId".
  • Go to page bindings page, add method action updateAllChildEmployeesRows  set the parameters value for empKeyList as "#{pageFlowScope.empList}" and departmentId as "#{pageFlowScope.departmentId}".
  • From component palette add two button as Save/Cancel and set the action to save/exit respectively.
Open the EmployeeEditBean page and add the below code.
public void getSelectedDeptId(ValueChangeEvent valueChangeEvent) {
        ADFContext.getCurrent().getPageFlowScope().put("departmentId", valueChangeEvent.getNewValue());
    }

Last step create the index.jspx page and drop EmployeeDetail-btf as region. Run the index.jspx page and page should look like below.


Select multiple employee rows and click on edit button.


Select the required DepartmentId and click on Save button will save the data in cache not to the DB. If Cancel button is clicked no action will happen, it will just close the popup window.


Now we can notice the departmentId for the selected employee rows as updated. In this page if Commit button is clicked DataControlFrame will update the child taskflow values to the database and Rollback button will undo all the changes.

No comments:

Post a Comment