Tuesday, December 11, 2012

Solr Search integration with ADF Faces - Part 1

Solr is the popular, blazing fast open source enterprise search platform from the Apache Lucene project. Its major features include powerful full-text search, hit highlighting, faceted search, dynamic clustering, database integration, rich document (e.g., Word, PDF) handling, and geospatial search.

Here why I'm trying to integrate Solr search with ADF Faces is, take a scenario where we need to build an EBook application having large data set. Each book may consists some 200 pages, If user wants to search some content inside the book or in cross books. It's very difficult to search in which page of the book the content exits and more over it will be overhead on DB server. By using Solr we can avoid this and do content indexing and do full-text also.

In Part 1, will learn on how to do content indexing. In my next post - Part 2, I will show how full-text search can be integrated with ADF application. Follow the article to install Solr server n windows, I have installed the Apache Solr 4.0 version, after the complete installation of jdk, tomcat, solr we can see the screen as follows, click on the collection1 schema and notice Num Docs will be 0.


Next download the SolrJ version 4.0, Solrj is a java client to access solr. It offers a java interface to add, update, and query the solr index.

Model Diagram: Download the sql script.


Next is to add the fields that will be indexed in Solr server, open the C:\solr\collection1\conf\schema.xml and alert the schema file by adding the below fields under <fields> tag. Before adding below fields shutdown the tomcat server.


Notice few fields will be already there, so add only missing fields then start the tomcat server and try to access the Solr admin. If the page doesn't load properly then schema file has some issue.

Create Fusion Web Application with entities based on category and product tables. Create a session bean and java client, add the following jar in "Project Properties-> Libraries and ClassPath":
  • apache-solr-solrj-4.0.0
  • commons-codec-1.3
  • commons-httpclient-3.1
  • commons-io-2.1
  • jcl-over-slf4j-1.6.4
  • slf4j-api-1.6.4
  • slf4j-jdk14-1.6.4
  • solr-solrj-1.4.0
All the above jar will be present in downloaded "apache-solr-4.0.0\dist\solrj-lib" directory, if you can't find all jar. You can download from the link.

Open the Java Client, add the below code.
private static void printProduct(Product product) throws MalformedURLException, SolrServerException, IOException {
        //Ip address is hard corded, where the Solr server is installed
        SolrServer server = new CommonsHttpSolrServer("http://10.177.252.178:8080/solr");
        SolrInputDocument doc = new SolrInputDocument();
        doc.addField("id", product.getId());
        doc.addField("title", product.getTitle());
        doc.addField("category", product.getCategoryRef().getName());
        doc.addField("productby", product.getProductBy());
        doc.addField("price", product.getPrice());
        doc.addField("description", product.getDescription());
        String features = product.getFeatures().replaceAll(";", " ");
        doc.addField("features", features);
        System.out.println("Content Indexing Started for Id " + product.getId());
        server.add(doc);
        server.commit();
        System.out.println("Content Indexing Completed for Id " + product.getId());
    }

Run the java client, all the records will get indexed into solr server. Now we can see on Solr admin homepage for collection1 schema 40 doc are added.

Tuesday, September 25, 2012

Oracle ADF Essentials - Faster and Simpler Java-based Application Development - Now Free

Oracle ADF Essentials is an end-to-end Java EE framework that simplifies application development by providing out-of-the-box infrastructure services and a visual and declarative development experience. Oracle ADF Essentials is free to develop and deploy.

More detail go through the link.

Thursday, September 20, 2012

Deploy BC4J/EJB application to GlassFish Server

Now deploying the ADF Faces application to the Glassfish server has become easy, users can directly  deploy the application from JDeveloper itself. Deploying the ADF Faces application to the Glassfish server directly runs with Oracle JDeveloper 11.1.2.3.0 onwards.

Note:- If application has model part then first configure JNDI DataSource, In my previous article I have explained how to "Configure JNDI DataSource for OracleDB in GlassFish Server". 

Implementation Steps for BC4J Application:-
  1. Create a fusion web application.
  2. Goto the ViewController project, right click and select project properties. In Project properties wizard select Deployment and edit the WAR deployment profile.
  3. In Platform selection choose Default Platform as "Glassfish 3.1".
  4. Next go to Application, right click and select project properties. In Project properties wizard select Deployment and edit the EAR deployment profile and in Platform selection choose Default Platform as "Glassfish 3.1".
  5. Open AppModule.xml file and select Configurations tab.
  6. Edit AppModuleLocal and modify the DataSource Name from "java:comp/env/jdbc/OracleDS" to "jdbc/OracleDS"
  7. Create Application Server Connection to Glassfish server.
  8. Select application, deploy the EAR file directly to the Glassfish server.
Note: - Above Default Platform determines the platform-specific behavior when packaging the archive for deployment to an Application Server.

Implementation Steps for EJB/JPA Application:-
  1. Create a fusion web application.
  2. Goto the Model project, right click and select project properties. In Project properties wizard select Deployment and edit the JAR deployment profile and in Platform selection choose Default Platform as "Glassfish 3.1".
  3. Goto the ViewController project, right click and select project properties. In Project properties wizard select Deployment and edit the WAR deployment profile and in Platform selection choose Default Platform as "Glassfish 3.1".
  4. Next go to Application, right click and select project properties. In Project properties wizard select Deployment and edit the EAR deployment profile and in Platform selection choose Default Platform as "Glassfish 3.1".
  5. Open persistence.xml file and select Model->Persistence Unit->Connection tab.
  6. In General selection, in JTA DataSource modify "java:/app/jdbc/jdbc/OracleDS" to "jdbc/OracleDS"
  7. In JTA Properties selection, in JTA DataSource Property modify "java:/app/jdbc/jdbc/OracleDS" to "jdbc/OracleDS"
  8. Application like EJB which requires jndi lookup's, we have to change the initial context factory class name. Open the DataControls.dcx file, In ejb-definition tag modify the initial-context-factory class name from "weblogic.jndi.WLInitialContextFactory" to "com.sun.enterprise.naming.SerialInitContextFactory"
  9. Create Application Server Connection to Glassfish server.
  10. Select application, deploy the EAR file directly to the Glassfish server.

Tuesday, September 4, 2012

EJB DataControl - JPA Single Table Inheritance

JPA supports three types of inheritance, In this article will try "single table inheritance using EJB datacontrol". For more information on JPA inheritance go through the link.

In single table inheritance, a single table is used to store all of the instances of the entire inheritance hierarchy. The table will have a column for every attribute of every class in the hierarchy. A discriminator column is used to determine which class the particular row belongs to, each class in the hierarchy defines its own unique discriminator value.

Table Structure: -


Model Diagram:-


In the above example the discriminator column (SEX) is added to the table to distinguish between the Men and Women instances.

You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.2.0 + HR Schema]
Note: - You can find the sql file in application JPASingleTableApp/etc/ folder

Implementation Steps:-

Lets create a Fusion Web Application with Entities based on Person, edit the Person.java entity and remove the menAttr, womenAttr attributes.  Add the below annotation in Person entity.

@Table(name="PERSON")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="SEX", discriminatorType=DiscriminatorType.STRING,length=1)

Create an entity Men by extending the Person and add the below code.
@Entity
@Table(name = "PERSON")
@DiscriminatorValue("M")
@NamedQueries( { @NamedQuery(name = "Men.findAll", query = "select o from Men o") })
public class Men extends Person implements Serializable {
    @Column(name = "MEN_ATTR")
    private String menAttr;

    public Men() {
    }

    public String getMenAttr() {
        return menAttr;
    }

    public void setMenAttr(String menAttr) {
        this.menAttr = menAttr;
    }
}

Create an entity Women by extending the Person and add the below code.
@Entity
@Table(name = "PERSON")
@DiscriminatorValue("W")
@NamedQueries( { @NamedQuery(name = "Women.findAll", query = "select o from Women o") })
public class Women extends Person implements Serializable {
    @Column(name = "WOMEN_ATTR")
    private String womenAttr;

    public Women() {
    }

    public String getWomenAttr() {
        return womenAttr;
    }

    public void setWomenAttr(String womenAttr) {
        this.womenAttr = womenAttr;
    }
}

Next create a Session Bean and data control for the Session Bean and in ViewController project create a jspx page and drop menFindAll, womenFindAll as separate Table->ADF Read only Table. Run the jspx page and web page will be displayed as shown in below image, table bound to menFindAll should display only men records and table bound to womenFindAll should display only women records.

Thursday, August 30, 2012

Configure JNDI DataSource for OracleDB in GlassFish Server

In this article will see how to "Configure JNDI DataSource for Oracle in GlassFish 3.2 Server".

Login to Glassfish admin console in browser window, Once its open, in the left-hand side panel under Resources, click on JDBC and then JDBC Connection Pools. In the right hand-side click on new button will open the "New JDBC Connection Pool" window, enter the details as shown in below image.


Note: To store, organize, and retrieve data, most applications use relational databases. Java EE applications access relational databases through the JDBC API. Before an application can access a database, it must get a connection. JDBC resources provide applications with a means to connect to a database.

Clicking on Next button will display as shown in below image.


In same "New JDBC Connection Pool" window, in Additional Property section add the properties details as shown in below image.


Click on ping to test if the pool is created successfully. If not successful, check all the properties again.


Now under Resources, click on JDBC and then JDBC Resources. In the right hand-side click on new button will open the "New JDBC Resource" window, enter the details as shown in below image.

Tuesday, August 7, 2012

How many rows Modified/Added in ADF Table

I was reading through OTN Discussion Forums where I found one topic "How many rows were Modified/Added in ADF Table by the user". Jobinesh has written article on "Displaying Transaction Log When User Commits a Transaction" which explains how to get the entity status, above example overrides EntityImpl::doDML(int operation, TransactionEvent e) method to track the status of entities.

I was trying to get Modified/Added rows in EJB DataControl but above suggested solution can't be implemented, because application module in not supported in EJB. So in this article I'm trying to get the entity status in backing bean using BC4J, in my next article I will try to explain on how to achieve the same scenario using EJB.

Results page looks like below.


You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.2.0 + HR Schema]

Implementation Steps:

Create Fusion Web Application with business components from tables based Employees table, open Employees.xml and select Java tab. Generate EmployeesImpl and add the below code in create method to generate sequence number for employee id using SequenceImpl class.
/**
 * Add attribute defaulting logic in this method.
 * @param attributeList list of attribute names/values to initialize the row
 */
protected void create(AttributeList attributeList) {
	super.create(attributeList);
	SequenceImpl seq = new SequenceImpl("EMPLOYEES_SEQ", getDBTransaction());
	Number seqNextval = seq.getSequenceNumber();
	setEmployeeId(seqNextval);
}
Open the EmployeesView and select Java tab, generate EmployeesViewImpl class.

In ViewController project, create index.jspx page and backingbean as "IndexBean" with scope as "ViewScope". Open the IndexBean and copy the below method code.
private RichTable empTable;
private List selectedEmpArray = new ArrayList();
private int modifiedRows = 0;
private int newRows = 0;

public IndexBean() {
}

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

public RichTable getEmpTable() {
	return empTable;
}

public void setModifiedRows(int modifiedRows) {
	this.modifiedRows = modifiedRows;
}

public int getModifiedRows() {
	return modifiedRows;
}

public void setNewRows(int newRows) {
	this.newRows = newRows;
}

public int getNewRows() {
	return newRows;
}

/**
 * Get the selected rowKey
 * Add the rowKey to selectedEmpArray arrayList
 * @param selectionEvent
 */
public void empSelectionEvent(SelectionEvent selectionEvent) {
	RowKeySet empRKS = selectionEvent.getAddedSet();
	if (empRKS.size() > 0) {
		Iterator empRKSIterator = empRKS.iterator();
		while (empRKSIterator.hasNext()) {
			Key key = (Key)((List)empRKSIterator.next()).get(0);
                        //Add the key if not existed in selectedEmpArray
			if (!selectedEmpArray.contains(key)) {
				selectedEmpArray.add(key);
			}
		}
	}
}

public BindingContainer getBindings() {
	return BindingContext.getCurrent().getCurrentBindingsEntry();
}

/**
 * While loop the selectedEmpArray
 * Get the entity status for the rowKeys
 * @param actionEvent
 */
public void fetchModifiedRows(ActionEvent actionEvent) {
	if (selectedEmpArray.size() > 0) {
		//Resetting the row counts
		this.setModifiedRows(0);
		this.setNewRows(0);

		DCBindingContainer dcBindings = (DCBindingContainer)getBindings();
		DCIteratorBinding EmpsDCIterBinding = dcBindings.findIteratorBinding("EmployeesView1Iterator");
		RowSetIterator EmpsRSIter = EmpsDCIterBinding.getRowSetIterator();
		ViewObject vo = EmpsDCIterBinding.getViewObject();

		Iterator selectedEmpsIter = selectedEmpArray.iterator();
		while (selectedEmpsIter.hasNext()) {
			Row currentRow = EmpsRSIter.getRow((Key)selectedEmpsIter.next());
			EmpsRSIter.setCurrentRow(currentRow);

			ViewRowImpl myRow = (ViewRowImpl)vo.getCurrentRow();
			EntityImpl entityImpl = (EntityImpl)myRow.getEntity(0);
			if (EntityImpl.STATUS_MODIFIED == entityImpl.getEntityState()) {
				this.setModifiedRows((this.getModifiedRows() + 1));
			} else if (EntityImpl.STATUS_NEW == entityImpl.getEntityState()) {
				this.setNewRows((this.getNewRows() + 1));
			}
		}
	}
	//Commit the transaction
	commitAction();
}

public String commitAction() {
	BindingContainer bindings = getBindings();
	OperationBinding operationBinding = bindings.getOperationBinding("Commit");
	Object result = operationBinding.execute();
	if (!operationBinding.getErrors().isEmpty()) {
		return null;
	}
	return null;
}

/**
 * CreateInsert new row and get rowKey for the added row
 * Add the rowKey to the selectedEmpArray arrayList
 * @param actionEvent
 */
public void createNewRow(ActionEvent actionEvent) {
	BindingContainer bindings = getBindings();
	OperationBinding oper = bindings.getOperationBinding("CreateInsert");
	oper.execute();

	DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
	DCIteratorBinding EmpsIter = dcBindings.findIteratorBinding("EmployeesView1Iterator");
	EmployeesViewImpl eImpl = (EmployeesViewImpl)EmpsIter.getViewObject();
        //Add the key if not existed in selectedEmpArray
	if (!selectedEmpArray.contains(eImpl.getCurrentRow().getKey())) {
		selectedEmpArray.add(eImpl.getCurrentRow().getKey());
	}
}
Note:- Here createInsert is model driven component, when user clicks on the Create button the selectionListener will not be executed for the first time. So in the above createNewRow method,  the rowKey value will be added to selectedEmpArray.

Open index.jspx page.
  • From datacontrol palette drag and drop EmployeesView1->Table as ADF Table with rowSelection as multiple.
  • Surround the table with panel collection and add toolbar in that.
  • Bind the empolyees table as binding="#{viewScope.IndexBean.empTable}"
  • Set the SelectionListener as "#{viewScope.IndexBean.empSelectionEvent}"
  • Go to Bindings tab and Create Action Binding, Data Collection as EmployeesView1 and Operation as CreateInsert.
  • From component palette drop af:commandButton and set actionListener as "#{viewScope.IndexBean.createNewRow}".
  • Go to Bindings tab and Create Action Binding, Data Collection as AppModuleDataControl and Operation as Commit.
  • From component palette drop af:commandButton and set actionListener as #{viewScope.IndexBean.fetchModifiedRows}, disabled as "#{!bindings.Commit.enabled}", partialSubmit as "true".
  • Add af:outputLabel and value as "No of Rows Modified -  #{viewScope.IndexBean.modifiedRows}", partialTriggers to commit button.
  • Add af:outputLabel and value as "No of Rows Newly added - #{viewScope.IndexBean.newRows}",  partialTriggers to commit button.

Thursday, July 12, 2012

Bean Data Control - Create Simple Search Form

In Oracle ADF, search form can be created with BC4J/EJB model using "View Criteria/Named Criteria" by dropping "af:query" component in view layer. Here in this article, I'm trying to build simple search form based on bean data control.

The Results page look like below.


Enter the Department Id in search form and click search button will filter results.


You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.0.0 (11g R2) + HR Schema]

Implementation Steps

Create Fusion Web application, in model project create Employees java class. Open Employees.java and add the below code.
public class Employees {
    private Integer employeeId;
    private String firstName;
    private String lastName;
    private String email;
    private String phoneNo;
    private Date hireDate;
    private String jobId;
    private Integer salary;
    private Integer departmentId;

    public Employees() {
        super();
    }

    public Employees(int employeeId, String firstName, String lastName, String email, String phoneNo, Date hireDate,
                     String jobId, int salary, int departmentId) {
        this.setEmployeeId(employeeId);
        this.setFirstName(firstName);
        this.setLastName(lastName);
        this.setEmail(email);
        this.setPhoneNo(phoneNo);
        this.setHireDate(hireDate);
        this.setJobId(jobId);
        this.setSalary(salary);
        this.setDepartmentId(departmentId);
    }

    public void setEmployeeId(Integer employeeId) {
        this.employeeId = employeeId;
    }

    public Integer getEmployeeId() {
        return employeeId;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

    public void setPhoneNo(String phoneNo) {
        this.phoneNo = phoneNo;
    }

    public String getPhoneNo() {
        return phoneNo;
    }

    public void setHireDate(Date hireDate) {
        this.hireDate = hireDate;
    }

    public Date getHireDate() {
        return hireDate;
    }

    public void setJobId(String jobId) {
        this.jobId = jobId;
    }

    public String getJobId() {
        return jobId;
    }

    public void setSalary(Integer salary) {
        this.salary = salary;
    }

    public Integer getSalary() {
        return salary;
    }

    public void setDepartmentId(Integer departmentId) {
        this.departmentId = departmentId;
    }

    public Integer getDepartmentId() {
        return departmentId;
    }
}
Create PopulateData java class, open PopulateData.java and add the below code.
public class PopulateData {
    private static List allEmployees;
    static {
        allEmployees = new ArrayList();
        allEmployees.add(new Employees(100, "Steven", "King", "SKING", "515.123.4567", new Date(), "AD_PRES", 24000,
                                       20));
        allEmployees.add(new Employees(101, "Neena ", "Kochhar", "NKOCHHAR", "515.123.4568", new Date(), "AD_VP",
                                       17000, 90));
        allEmployees.add(new Employees(102, "Lex", "De Haan", "AHUNOLD", "515.423.4567", new Date(), "IT_PROG", 15000,
                                       90));
        allEmployees.add(new Employees(103, "Alexander", "Hunold", "AHUNOLD", "515.423.4567", new Date(), "IT_PROG",
                                       10000, 20));
        allEmployees.add(new Employees(104, "Bruce", "Ernst", "BERNST", "515.423.4568", new Date(), "IT_PROG", 5000,
                                       50));
        allEmployees.add(new Employees(105, "David", "Austin", "DAUSTIN", "515.423.4569", new Date(), "IT_PROG", 7000,
                                       30));
        allEmployees.add(new Employees(106, "Valli", "Pataballa", "VPATABAL", "515.423.4560", new Date(), "IT_PROG",
                                       8000, 50));
        allEmployees.add(new Employees(107, "Diana", "Lorentz", "DLORENTZ", "515.423.5567", new Date(), "IT_PROG",
                                       9000, 50));
        allEmployees.add(new Employees(108, "Nancy", "Greenberg", "NGREENBE", "515.124.4569", new Date(), "FI_MGR",
                                       10000, 100));
        allEmployees.add(new Employees(109, "Daniel", "Faviet", "DFAVIET", "515.124.4169", new Date(), "FI_ACCOUNT",
                                       13000, 100));
        allEmployees.add(new Employees(110, "John", "Chen", "JCHEN", "515.124.4269", new Date(), "FI_ACCOUNT", 14000,
                                       100));
        allEmployees.add(new Employees(111, "Ismael", "Sciarra", "ISCIARRA", "515.124.4369 ", new Date(), "FI_ACCOUNT",
                                       12000, 60));
        allEmployees.add(new Employees(112, "Jose Manuel", "Urman", "JMURMAN", "515.124.4469", new Date(),
                                       "FI_ACCOUNT", 4000, 60));
        allEmployees.add(new Employees(112, "Jose Manuel", "Urman", "JMURMAN", "515.124.4469", new Date(),
                                       "FI_ACCOUNT", 7800, 60));
        allEmployees.add(new Employees(113, "Luis", "Popp", "LPOPP", "515.124.4567", new Date(), "FI_ACCOUNT", 6900,
                                       70));
        allEmployees.add(new Employees(114, "Den", "Raphaely", "DRAPHEAL", "515.127.4561", new Date(), "PU_MAN", 11000,
                                       90));
    }

    public PopulateData() {
        super();
    }

    public static List getAllEmployees() {
        return allEmployees;
    }
}
Create EmployeeServicejava class, open EmployeeService.java and add the below code. Create data control from EmployeeService Bean class.
public List filterByDeptId(Integer departmentId) {
        try {
            List allEmployees = PopulateData.getAllEmployees();
            if (departmentId != null) {
                List filteredEmployees = new ArrayList();
                for (Employees e : allEmployees) {
                    if (e.getDepartmentId() == departmentId) {
                        filteredEmployees.add(e);
                    }
                }
                return filteredEmployees;
            } else {
                return allEmployees;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
}

public List getAllEmployees() {
    return PopulateData.getAllEmployees();
}
In ViewController project, create jspx page and from data control palette
  1. Drop filterByDeptId as ADF Method Parameter
  2. Drop filterByDeptId->Employees->Table as ADF Read-only Table

Monday, July 2, 2012

How to add a new row at the end of the ADF Table

I was reading through OTN Discussion Forums where I found one topic "How to add a new row at the end of the ADF Table", I found couple of articles which explains how to insert new rows to the end of ADF Table. Here is the list below.

Here is one more way to insert new rows to the end of ADF table by overriding the view object's(ViewImpl) insertRow() method.
/**
 * This method will overrides the view object's insertRow()
 * First create a new row
 * Removes the row from the collection and then retain it for insertion into another location.
 * Navigates to the last row in the row set.
 * Navigates to next row
 * Inserts the same row to the row set
 * @param row
 */
@Override
public void insertRow(Row row) {
 super.insertRow(row);
 row.removeAndRetain();
 last();
 next();
 getDefaultRowSet().insertRow(row);
}

You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.0.0 (11g R2) + HR Schema]

Run the index.jspx page and navigate the departments records, now click on Create button. Notice in employee table new row is added to end of the ADF Table.

Monday, June 25, 2012

Get JPA Entity Attributes programmatically in managed bean

Sometimes it is necessary to get database table field column names for further functional process in applications. In one my previous article I have explained one of the use case - Configure Comparison of Row Objects at Run Time, here application module(BC4J) is used to access the ViewObject attributes.

In EJB data control doesn't create any application module or neither have access to application module Api's directly. When the ViewObject is accessed based on the Iterator a dummy object is created using DCDataVo Api. DCDataVo provides little leverage to access certain model layer functionality, so that all Adapter Data Control can use the DCDataVo api to extend to build custom features.

You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.0.0 (11g R2) + HR Schema]

Here in the below code JPA entity attributes are accessed in managed bean using DCDataVo api.

/**
 * This method will get the View Object based on Iterator
 * Reads the entity attributes(column names)
 * @param actionEvent
 */
public void compareAttributes(ActionEvent actionEvent) {
 DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
 DCIteratorBinding deptIter = dcBindings.findIteratorBinding("employeesFindAllIterator");
 ViewObject vo = deptIter.getViewObject();
 int count = 0;
 AttributeDef[] attrDefs = vo.getAttributeDefs();
 for (AttributeDef attrDef : attrDefs) {
  byte attrKind = attrDefs[count].getAttributeKind();
  //Condition to exclude the fk attributes
  if (attrKind == 1) {
   System.out.println(attrDef.getName());
   count++;
  }
 }
}

Displaying the Employee entity attributes.

Simulate JPA Dynamic Query Using View Criteria

Recently I'm working in an application where JPA entity has a named query with parameters passed to that query and exposed as EJB data control, here parameter is passed dynamically at run time. Now same data control is dropped as ADF table, where user can add new rows to the table and perform merge/persist operations.

JPA API provides an alternative way for defining JPA queries, which is mainly useful for building dynamic queries whose exact structure is only known at run time. Building a dynamic query based on fields that a user fills at run time in a form that contains many optional fields. 


In this article, I'm trying to Simulate JPA Dynamic Query Using View Criteria to filter ADF Table and perform merge/persist operations on ADF Table. Below solution is the workaround to achieve the above scenario using EJB data control.

In EJB data control doesn't create any application module or neither have access to application module Api's directly. When the ViewObject is accessed based on the Iterator a dummy object is created using DCDataVo Api.  DCDataVo provides little leverage to access certain model layer functionality, so that all Adapter Data Control can use the DCDataVo api to extend to build custom features.

You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.0.0 (11g R2) + HR Schema]

Implementation Steps

Create Fusion Web Application with entities based on Emplo, then create a session bean and data control for the session bean.

In the ViewController create index.jspx page and create a IndexBean.java as backing bean, follow the below steps:
  • From data control palette drag and drop EmployeesFindAll()->Table as ADF Table.
  • Drop  EmployeesFindAll()->Operations->Create as ADF Button, name as "Commit".
  • Drop the persistEmployees as ADF button and bind the value to "#{bindings.employeesFindAllIterator.currentRow.dataProvider}"
  • From component palette drop Input Text and label as "Department Id"
  • Drop Button, name as "Search" and ActionListener method as "executeSearch"
Open the IndexBean.java and copy the below method code.

/**
 * This method will get the View Object based on Iterator
 * Creates the view crietria at the runtime,
 * Set the Operator type and pass the paramater 
 * Execute the View Object, print results to the web screen.
 * @param actionEvent
 */
public void executeSearch(ActionEvent actionEvent) {
 DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
 DCIteratorBinding deptIter = dcBindings.findIteratorBinding("employeesFindAllIterator");
 // Getting the dummy ViewObject based on Iterator
 ViewObject vo = deptIter.getViewObject();
 // Create the viewCriteria at runtime
 ViewCriteria vc = vo.createViewCriteria();
 ViewCriteriaRow vcRow = vc.createViewCriteriaRow();
 // ViewCriteriaRow attribute value requires operator and value.
 // Note also single-quotes around string value.
 ViewCriteriaItem vcRowItem = vcRow.ensureCriteriaItem("departmentId");
 vcRowItem.setOperator("=");
 vcRowItem.getValues().get(0).setValue(this.getDeptId().getValue());
 vc.addElement(vcRow);
 vo.applyViewCriteria(vc);
 // Execute the query
 vo.executeQuery();
}

Run the index.jspx page and result page looks like below.


Now enter department Id in input text field and click on search button, Employees table will be filtered by department id. Now click on create button to add new rows to the particular searched department, notice a new row is created in the table. Enter the field values and save the record, here if you want to generate employee id as auto generated one then configure @Table Generator for employee id. 

Thursday, June 7, 2012

Display ADF Dynamic Table based on Declarative SQL Mode In View Object

While working in my previous article "Configure Comparison of Row Objects at Run Time", I struck in constructing the dynamic SQL statement, where I needed to pass the selected column attributes at run time and displays the results using ADF Dynamic Table.

ADF Business Components support constructing design time and run time SQL statements known as "Declarative SQL Mode". Please refer 5.8 Working with View Objects in Declarative SQL Mode in Fusion Developer's Guide to learn more about Declarative View Objects.

Declarative SQL Mode generates SELECT and from options, where required column attributes should be populated in programmatic way. So by this way we can control which are the required column attributes to be queried.

Note: - But ADF Dynamic Table in view layer will not have any clue, which are the attributes selected in model layer. So ADF Dynamic Table will still display all the attributes in view layer. In this article, I'm showing how can we "Display ADF Dynamic Table based on Declarative SQL Mode In View Object". 

So the out come of this scenario looks like below. In the webpage employees details will be displayed with all attributes. Click on the "processDeclarativeSqlQuery" button.


Notice in the below image, only three attributes will be displayed which are passed as dynamic attributes to the SQL query at run time.


You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.0.0 (11g R2) + HR Schema]

The below approach is one of the way to handle the dynamic attributes passed to SQL query at run time and display only those attributes in view layer using ADF Dynamic Table.

Implementation Steps

Create Fusion Web Application with business components from tables based Employees table, open EmployeesView.xml and select Query tab. Click on Edit SQL Query and select the mode as "Declarative" as shown in the below image.


Select the Attribute tab and uncheck "Selected in Query" options for all the Attributes.

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

Note: - Below attribute values are hard coded.
/**
 * This method will execute the Declarative Sql mode by filtering
 * required attributes in result set.
 **/
public void processDeclarativeSqlQuery() {
 ViewObjectImpl empVoImpl = this.getEmployeesView1();
 empVoImpl.resetSelectedAttributeDefs(false);
 empVoImpl.selectAttributeDefs(new String[] { "FirstName", "LastName", "Email" });
 System.out.println(empVoImpl.getQuery());
 empVoImpl.executeQuery();

        //Since ADF Dynamic Table in view layer will not have any clue, which are the attributes selected in model layer
        //So updating the displayHint values as "Hide", this value can be checked in view layer using column rendered property
 ArrayList attrList = new ArrayList();
 attrList.add("FirstName");
 attrList.add("LastName");
 attrList.add("Email");
 ViewAttributeDefImpl[] attrDefs = empVoImpl.getViewAttributeDefImpls();
 for (ViewAttributeDefImpl attrDef : attrDefs) {
  byte attrKind = attrDef.getAttributeKind();
  //checks attribute kind for each element in an array of AttributeDefs
  if (attrKind != AttributeDef.ATTR_ASSOCIATED_ROW && attrKind != AttributeDef.ATTR_ASSOCIATED_ROWITERATOR) {
   String columnName = attrDef.getName();
   if (!attrList.contains(columnName)) {
    //Setting the displayHint property value as "HIDE"
    attrDef.setProperty("DISPLAYHINT", "HIDE");
   }
  }
 }
}
Go back to AppModule.xml and select Java tab in client interface section move processDeclarativeSqlQuery, from available to selected block.

In ViewController project, create index.jspx page.
  • From data control palette drag and drop EmployeesView1->Table as ADF Read Only Dynamic Table, set RowSelection as "multiple" and surround the table with panel collection component.
  • From data control palette drop processDeclarativeSqlQuery as ADF Button and set the partial trigger to the employees table.
  • Set the rendered property for the column as "#{bindings.EmployeesView1.hints[def.name].displayHint  eq 'Display'}".
Employees Dynamic Table code will looks like below.
<af:table rows="#{bindings.EmployeesView1.rangeSize}"
    fetchSize="#{bindings.EmployeesView1.rangeSize}"
    emptyText="#{bindings.EmployeesView1.viewable ? 'No data to display.' : 'Access Denied.'}"
    var="row" rowBandingInterval="0"
    value="#{bindings.EmployeesView1.collectionModel}"
    selectedRowKeys="#{bindings.EmployeesView1.collectionModel.selectedRow}"
    selectionListener="#{bindings.EmployeesView1.collectionModel.makeCurrent}"
    rowSelection="multiple" id="t1" styleClass="AFStretchWidth" partialTriggers="::cb1">
 <af:forEach items="#{bindings.EmployeesView1.attributeDefs}" var="def">
  <af:column headerText="#{bindings.EmployeesView1.labels[def.name]}" sortable="true"
       sortProperty="#{def.name}" id="c1"
       rendered="#{bindings.EmployeesView1.hints[def.name].displayHint  eq 'Display'}">
   <af:outputText value="#{row[def.name]}" id="ot1"/>
  </af:column>
 </af:forEach>
</af:table>

Sunday, June 3, 2012

Configure Comparison of Row Objects at Run Time

Recently I was working in an application where I need to compare two or more row objects based on selected attributes. In this article I will explain one of the way to configure comparison of row objects at run time, so this pattern will give users the ability to select row objects for comparison and to define which of the available attributes they want to compare at run time.

So the out come of this scenario looks like below. In result page car details table will be displayed, user can select the multiple rows for comparison and click on Compare button.


Next screen will display the shuttle component, where it defines which are the attributes included in the comparison. So user can move attributes from available attributes to selected attributes to compare and click on the Process button.


Note:- In above shuttle component attributes are read from ViewObject programmatically, article written in one of my previous blog - Get ViewObject attributes are  read programmatically and display these attributes in ADF Shuttle component.

Final comparison results table, notice all selected attributes will be showed as first column values and also selected row objects are displayed.


Note:- Above dynamic table is used to display Cars Features Comparison, I followed the solution written on AMIS Technology Blog regarding Creating ADF Faces Dynamic Table with Head to Head comparison using managed bean.

You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.0.0 (11g R2)]

Implementation steps

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


Note: Download the application from above link and unzip the application. schema.sql file is in application etc folder.

Open the childCarsView, select Query tab and create bind variable "Bind_CarsID".


Alter the query as below.
SELECT Cars.ID, 
       Cars.NAME, 
       Cars.MSRP, 
       Cars.BASE_ENGINE, 
       Cars.CYLINDERS, 
       Cars.DRIVE_TYPE, 
       Cars.FUEL_CAPACITY, 
       Cars.FUEL_ECONOMY, 
       Cars.FUEL_TYPE, 
       Cars.HORSE_POWER, 
       Cars.TORQUE, 
       Cars.TRANSMISSION
FROM CARS Cars
WHERE Cars.ID in (select regexp_substr(:Bind_CarsID,'[^,]+', 1, level) 
   from dual 
    connect by 
        regexp_substr(:Bind_CarsID, '[^,]+', 1, level) 
            is not null)
Arun Ramamoorthy has explained well in article how to split comma separated string and pass to IN clause of select statement.

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


Create a CompareAttributes.java and paste the below code.
public class CompareAttributes {
    private String columnName;
    private String columnAliasName;

    public CompareAttributes() {
        super();
    }

    public CompareAttributes(String columnName, String columnAliasName) {
        super();
        this.columnName = columnName;
        this.columnAliasName = columnAliasName;
    }

    public void setColumnName(String columnName) {
        this.columnName = columnName;
    }

    public String getColumnName() {
        return columnName;
    }

    public void setColumnAliasName(String columnAliasName) {
        this.columnAliasName = columnAliasName;
    }

    public String getColumnAliasName() {
        return columnAliasName;
    }
}
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 add the below methods code.
/**
 * This method reads the ViewObject attributes and populate CompareAttributes list
 * @return CompareAttributes list
 */
public List getCompareAttributes() {
	ViewObjectImpl vo = getCarsView1();
	ViewAttributeDefImpl[] attrDefs = vo.getViewAttributeDefImpls();
	int count = 0;
	List compareAttrs = new ArrayList();
	for (ViewAttributeDefImpl attrDef : attrDefs) {
		byte attrKind = attrDefs[count].getAttributeKind();
		//checks attribute kind for each element in an array of AttributeDefs
		if (attrKind != AttributeDef.ATTR_ASSOCIATED_ROW && attrKind != AttributeDef.ATTR_ASSOCIATED_ROWITERATOR) {
			String columnName = attrDef.getName();
			String columnAliasName = attrDef.getAliasName();
			//Excluding the Id and Name fields
			if (!columnName.equals("Id") && !columnName.equals("Name")) {
				compareAttrs.add(new CompareAttributes(columnName, columnAliasName));
			}
			count++;
		}
	}
	return compareAttrs;
}

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

/**
 * This method will populate the childCarsView 
 * carRowKeysList contains CARS table primiary key - Id and
 * which will be passed as In Clause params 
 * @param carRowKeysList
 */
public void populateSelectedRows(List carRowKeysList) {
	if (carRowKeysList != null && carRowKeysList.size() > 0) {
		//Clean up the childCarsView 
		cleanUpChildCarsView1();
		String carsSelectedString = "";
		ViewObject childcarsVo = getChildCarsView1();
		for (int i = 0; i < carRowKeysList.size(); i++) {
			carsSelectedString += carRowKeysList.get(i) + ",";
		}
		childcarsVo.setNamedWhereClauseParam("Bind_CarsID",
											 carsSelectedString.substring(0, (carsSelectedString.length() - 1)));
		childcarsVo.executeQuery();
	}
}
Go back to AppModule.xml and select Java tab in client interface section move getCompareAttributes and populateSelectedRows from available to selected block.


In ViewController project, create a bounded taskflow as "Compare-btf". Open the Compare-btf taskflow, from component palette drop views as "CarsDetails", "CarsCompare". Generate CarsDetails.jsff, CarsCompare.jsff pages and "CarsDetailsBean.java", "CarsCompareBean.java" respectively. Add the EmployeeDetailsBean.java, CarsCompareBean.java  file in taskflow with scope as viewScope.

Draw the control flow case from CarsDetails to CarsCompare, outcome as compare and draw the control flow case from CarsCompare to CarsDetails, outcome as back.

Open CarsDetails.jsff page
  • From data control palette drag and drop CarsView1->Table as ADF Read Only Table, set RowSelection as "muliptle".
  • Surround the table with panel collection component and add Compare button with text as "Compare". 
  • Create the actionListener method for Compare button as "fectchSelectedCarKeys" and action as "compare".
  • Add the binding for table as "carsTable".
Open the CarsDetailsBean.java file and copy the below methods code.
/**
 * This method will get selected rows primiary key - Id values
 * @param actionEvent
 */
public void fectchSelectedCarKeys(ActionEvent actionEvent) {
	RowKeySet selectedCars = getCarsTable().getSelectedRowKeys();
	if (selectedCars.size() > 0) {
		DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
		DCIteratorBinding deptIter = dcBindings.findIteratorBinding("CarsView1Iterator");
		RowSetIterator deptRSIter = deptIter.getRowSetIterator();


		Iterator selectedCarsIter = selectedCars.iterator();
		List selectedRowKeys = new ArrayList();
		while (selectedCarsIter.hasNext()) {
			Key key = (Key)((List)selectedCarsIter.next()).get(0);
			Row currentRow = deptRSIter.getRow(key);
			selectedRowKeys.add(currentRow.getAttribute("Id"));
		}
		ADFContext.getCurrent().getPageFlowScope().put("selectedCarsRowKeys", selectedRowKeys);
	}
}
Open CarsCompare.jsff page.
  • From component palette drop the shuttle component and Bind to list value as "#{viewScope.CarsCompareBean.shuttleList}", looks like below code.
  • Add the shuttle component value as "#{viewScope.CarsCompareBean.selectedAttributes}"
  • Go to Bindings tab and create method action for "getCompareAttributes".
<af:selectManyShuttle label="" id="sms1" leadingHeader="Available Attributes"
					  trailingHeader="Selected Attributes"
					  value="#{viewScope.CarsCompareBean.selectedAttributes}" valuePassThru="true">
	<f:selectItems value="#{viewScope.CarsCompareBean.shuttleList}" id="si1"/>
</af:selectManyShuttle>
  • Add the button, name as "Process" and  create the actionListener method as "processAction".
  • Go to Bindings tab and create method action for "populateSelectedRows".
  • Create a Tree bindings as shown below.
  • Add the below dynamic table code to the page
<af:table var="row" rowBandingInterval="0" value="#{viewScope.CarsCompareBean.rows}" rowSelection="single"
		  id="t1b" styleClass="AFStretchWidth" partialTriggers=":::cb1">
	<af:forEach items="#{viewScope.CarsCompareBean.columns}" var="col">
		<af:column headerText="#{col['label']}" sortable="true" sortProperty="#{'name'}" id="c1b">
			<af:outputText value="#{row[col['name']]}" id="ot1b"/>
		</af:column>
	</af:forEach>
</af:table>
Open the CarsCompareBean.java and add the below methods code.
//Holds selected values in shuttle component
List selectedAttributes;
//Populates the shuttle component
List shuttleList;
//Populates the comparison table columns
private List> columns;
//Populates the comparison table rows
private List> rows;

public CarsCompareBean() {
}

public void setSelectedAttributes(List selectedAttributes) {
	this.selectedAttributes = selectedAttributes;
}

public List getSelectedAttributes() {
	return selectedAttributes;
}

public void setShuttleList(List shuttleList) {
	this.shuttleList = shuttleList;
}

public BindingContainer getBindings() {
	return oracle.adf.model.BindingContext.getCurrent().getCurrentBindingsEntry();
}

/**
 * This method will read the attributes from CarsView1
 * and popluate the shuttle component
 * @return
 */
public List getShuttleList() {
	BindingContainer bindings = getBindings();
	OperationBinding operBind = bindings.getOperationBinding("getCompareAttributes");
	List compareAttr = (List)operBind.execute();

	List shuttleList = new ArrayList();
	for (int i = 0; i < compareAttr.size(); i++) {
		CompareAttributes attr = compareAttr.get(i);
		SelectItem item = new SelectItem(attr.getColumnName(), attr.getColumnName(), attr.getColumnAliasName());
		shuttleList.add(item);
	}
	return shuttleList;
}

/**
 * This method will populate the dynamic table
 * @param actionEvent
 */
public void processAction(ActionEvent actionEvent) {
	List selectedCarsKeysList = (List)ADFContext.getCurrent().getPageFlowScope().get("selectedCarsRowKeys");
	BindingContainer bindings = getBindings();
	OperationBinding operBind = bindings.getOperationBinding("populateSelectedRows");
	operBind.getParamsMap().put("carRowKeysList", selectedCarsKeysList);
	operBind.execute();
	
	//Calling the populateDynamicTable
	populateDynamicTable();
}

/**
 * This method will populate the dynamic table based in selected row Objects
 * and attributes selected for comparison
 */
private void populateDynamicTable() {
	List attributes = getSelectedAttributes();
	columns = new ArrayList>();
	rows = new ArrayList>();

	Map column = new HashMap();
	column.put("label", "Cars Name");
	column.put("name", "header");
	columns.add(column);

	for (int i = 0; i < attributes.size(); i++) {
		Map row = new HashMap();
		row.put("header", attributes.get(i).toString());
		row.put("attribute", attributes.get(i).toString());
		rows.add(row);
	}
	DCBindingContainer dcBindings = (DCBindingContainer)getBindings();
	DCIteratorBinding iter = dcBindings.findIteratorBinding("ChildCarsView1Iterator");
	Row[] depts = iter.getAllRowsInRange();
	for (Row dept : depts) {
		column = new HashMap();
		column.put("label", dept.getAttribute("Name").toString());
		column.put("name", dept.getAttribute("Name").toString());
		columns.add(column);
		for (Map trow : rows) {
			trow.put(column.get("name"), dept.getAttribute((String)trow.get("attribute")));
		}
	}

}

public void setColumns(List> columns) {
	this.columns = columns;
}

public List> getColumns() {
	return columns;
}

public void setRows(List> rows) {
	this.rows = rows;
}

public List> getRows() {
	return rows;
}
Last step create a index.jspx page and drop Compare-btf as region into the page.

Friday, June 1, 2012

Bean DataControl - Create new table rows based on existing row content

The Create a Duplicate row enables the user to create an object by duplicating an existing object. The duplication aids the user by prefilling some values. The user has full control and can change any of the values during the creation process. So in this article, I'm will show how can we create new table rows based on existing rows using Bean Data Control.

This can be achieved in BC4J using "Create With Parameters" operation, there is an example written by Frank Nimphius in ADF code corner "How-to declaratively create new table rows based on existing row content".

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

At runtime page looks like below, you can now select a table row and press the "Copy Row" button. New row will be created by prefilling the selected row values.


Implementations Steps:

There are lot of ways to create a new in an iterator and populate the values from existing row, below I'm showing one of the way to achieve that.

Create Fusion Web Application, create the DeptBean.java and GenericBean.java classes in model project. Open the DeptBean.java and paste the below code.
public class DeptBean {
    private String deptNo;
    private String deptName;
    private String deptLocation;

    public DeptBean() {
        super();
    }

    public DeptBean(String deptNo, String deptName, String deptLocation) {
        this.deptNo = deptNo;
        this.deptName = deptName;
        this.deptLocation = deptLocation;
    }

    public void setDeptNo(String deptNo) {
        this.deptNo = deptNo;
    }

    public String getDeptNo() {
        return deptNo;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptLocation(String deptLocation) {
        this.deptLocation = deptLocation;
    }

    public String getDeptLocation() {
        return deptLocation;
    }
}
Open the GenericBean.java and paste the below code. Here static values are populated to show in result list.
/**
 * Populating Dept static values to DeptBean
 * @return DeptBean list
 */
    public List populateBean() {
        System.out.println("populateBean");
        List deptList = new ArrayList();
        {
            DeptBean dept = new DeptBean("10", "Administrator", "1700");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("20", "Marketing", "1800");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("30", "Purchasing", "1700");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("40", "Human Resource", "2400");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("50", "Shippings", "1500");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("60", "IT", "1400");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("70", "Public Relations", "2700");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("80", "Sales", "2500");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("90", "Executive", "1700");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("100", "Finance", "1700");
            deptList.add(dept);
        }
        {
            DeptBean dept = new DeptBean("110", "Accounting", "1700");
            deptList.add(dept);
        }
        return deptList;
    }

Now right click on GenericBean.java file and generate data control. Open the DataControl.dcx file in application navigator and select the GenericBean. Go to the GenericBean property inspector and set SupportsTransactions as true.

In ViewController project, create index.jspx page and "IndexBean.java". Add the  IndexBean.java file in adfc-config.xml file with scope as viewScope.

Open the IndexBean.java file and paste the below code.
private String deptNo;
private String deptName;
private String deptLocation;

public IndexBean() {
}

public void setDeptNo(String deptNo) {
 this.deptNo = deptNo;
}

public String getDeptNo() {
 return deptNo;
}

public void setDeptName(String deptName) {
 this.deptName = deptName;
}

public String getDeptName() {
 return deptName;
}

public void setDeptLocation(String deptLocation) {
 this.deptLocation = deptLocation;
}

public String getDeptLocation() {
 return deptLocation;
}

public void populateCopyRow(ActionEvent actionEvent) {
 //Executing the Create Method
 BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
 OperationBinding operBind = bindings.getOperationBinding("Create");
 operBind.execute();

 DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
 DCIteratorBinding customIter = dcBindings.findIteratorBinding("populateBeanIterator");
 RowSetIterator customRSIter = customIter.getRowSetIterator();
 //Populating the data to newly created row
 Row currentRow = customRSIter.getCurrentRow();
 System.out.println(this.getDeptName());
 currentRow.setAttribute("deptNo", this.getDeptNo());
 currentRow.setAttribute("deptName", this.getDeptName());
 currentRow.setAttribute("deptLocation", this.getDeptLocation());
}

public void copySelectedRow(SelectionEvent selectionEvent) {
 RowKeySet rks = selectionEvent.getAddedSet();
 if (rks != null) {
  System.out.println("Selected Row:");
  RichTable fowTable = (RichTable)selectionEvent.getComponent();
  Iterator iter = rks.iterator();
  while (iter.hasNext()) {
   fowTable.setRowKey(iter.next());
   FacesCtrlHierNodeBinding rowData = (FacesCtrlHierNodeBinding)fowTable.getRowData();
   this.setDeptNo(rowData.getAttribute("deptNo").toString());
   this.setDeptName(rowData.getAttribute("deptName").toString());
   this.setDeptLocation(rowData.getAttribute("deptLocation").toString());
  }
 }
}
Open the index.jspx page and follow the below steps:

  • From data control palette drag and drop populateBean->DeptBean->Table as ADF Table, set RowSelection as "single". 
  • Surround the Table with af:panelCollection component and  has a toolbar facet to which a a single toolbar button is added to initiate the "Copy Row". The button ID is referenced in the table's "partialTrigger" property so that clicking the button initiates a table refresh
  • Map the "Copy Row" ActionListener to "#{viewScope.IndexBean.populateCopyRow}" 
  • Bind the table selectionListener to "#{viewScope.IndexBean.copySelectedRow}"
  • Go to the page bindings and Create Action Binding as show below.

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.