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