Thursday Jan 17, 2013

How to create cascading (depending) auto suggest behavior using BC4J

In continuation to my previous blog on "How to create multi level cascading (dependent) list of values using BC4J", i am trying to leverage a similar usecase using af:autoSuggestBehavior in this article. Though the usecase looks same, the implementation is slightly different for this.

Let us assume a usecase where we have some text fields with auto suggest feature enabled, and their suggested items could be dependent on one other. For ex : Country, State and City.

This could be modeled by having an EO and VO created based on Person table and read-only look-up VOs created based on Country, State and City tables.

model.jpg

VOs.jpg

Implementing the dependency between LOVs is pretty straight forward. However, implementing the dependency between the auto suggest items is not. In order to achieve the dependency, first we would need couple of methods returning current row's CountryId and StateId. We could add them up in the AMImpl class.

public Number getCurrentCountryId(){
return (Number) this.getPersonView1().getCurrentRow().getAttribute("CountryId");
}

public Number getCurrentStateId(){
return (Number) this.getPersonView1().getCurrentRow().getAttribute("StateId");
}

As we need to filter out the States based on the Country and the City based on the Country & State, we would need to modify the query of these two VOs to include a bind variable in the where clause.

whereClause.jpg

For having the auto suggest, we need to have a view criteria defined for all the three look-up VOs (CountryView, StateView and CityView).

CountryVC.jpg

StateVC.jpg

CityVC.jpg

Generate VOImpl classes for Country, State and City VOs (with Include bind variable accessors option checked), and then expose setBindCountryName (in Country VO), setBindStateName (in State VO) and setBindCityName (in City VO) methods as client interfaces.

And the last part on the model is to pass the current row's CountryId and StateId to the Bind Variables defined in the State and City VOs. Also, we need to get the VCs created above to be executed by default (By editing the VO instance in the AM's data model), so that the auto suggest list would be filtered as and when the users type.

CountryViewAM.jpg

StateViewAM.jpg

CityViewAM.jpg

Here, we specify groovy expression for the CountryId and StateId as adf.object.applicationModule.<methodName>. For more information about using groovy expressions, check out this : http://www.oracle.com/technetwork/developer-tools/jdev/introduction-to-groovy-128837.pdf .

With this, we are done with setting up the model layer for the auto suggest dependency.

In the View layer, we would create an ADF Form based on the Person VO, with all the navigation buttons.

PersonJSPX.jpg

In order to construct the onSuggest items, we would create Tree Bindings for Country VO, State VO and City VO, along with method action bindings for the setBindCountryName, setBindStateName and setBindCityName methods.

pagedef.jpg

Now, we could add af:autoSuggestBehavior for CountryId, StateId and CityId fields. Then, add onSuggest methods in backing bean for populating the on suggest items for each fields.

onSuggest method for Country field :

public List onCountrySuggest(String searchCountryName) {
ArrayList<SelectItem> selectItems = new ArrayList<SelectItem>();

System.out.println(searchCountryName);
//get access to the binding context and binding container at runtime
BindingContext bctx = BindingContext.getCurrent();
BindingContainer bindings = bctx.getCurrentBindingsEntry();
//set the bind variable value that is used to filter the View Object
//query of the suggest list. The View Object instance has a View
//Criteria assigned
OperationBinding setVariable = (OperationBinding) bindings.get("setBind_CountryName");
setVariable.getParamsMap().put("value", searchCountryName);
setVariable.execute();
//the data in the suggest list is queried by a tree binding.
JUCtrlHierBinding hierBinding = (JUCtrlHierBinding) bindings.get("CountryView1");


//re-query the list based on the new bind variable values
hierBinding.executeQuery();

//The rangeSet, the list of queries entries, is of type
//JUCtrlValueBndingRef.
List<JUCtrlValueBindingRef> displayDataList = hierBinding.getRangeSet();

for (JUCtrlValueBindingRef displayData : displayDataList){
Row rw = displayData.getRow();
//populate the SelectItem list
selectItems.add(new SelectItem(
(Integer)rw.getAttribute("Id"),
(String)rw.getAttribute("Name")));
}

return selectItems;
}

onSuggest method for State field :


public List onStateSuggest(String searchStateName) {
ArrayList<SelectItem> selectItems = new ArrayList<SelectItem>();

System.out.println(searchStateName);
//get access to the binding context and binding container at runtime
BindingContext bctx = BindingContext.getCurrent();
BindingContainer bindings = bctx.getCurrentBindingsEntry();
//set the bind variable value that is used to filter the View Object
//query of the suggest list. The View Object instance has a View
//Criteria assigned
OperationBinding setVariable = (OperationBinding) bindings.get("setBind_StateName");
setVariable.getParamsMap().put("value", searchStateName);
setVariable.execute();
//the data in the suggest list is queried by a tree binding.
JUCtrlHierBinding hierBinding = (JUCtrlHierBinding) bindings.get("StateView1");


//re-query the list based on the new bind variable values
hierBinding.executeQuery();

//The rangeSet, the list of queries entries, is of type
//JUCtrlValueBndingRef.
List<JUCtrlValueBindingRef> displayDataList = hierBinding.getRangeSet();

for (JUCtrlValueBindingRef displayData : displayDataList){
Row rw = displayData.getRow();
//populate the SelectItem list
selectItems.add(new SelectItem(
(Integer)rw.getAttribute("Id"),
(String)rw.getAttribute("Name")));
}

return selectItems;
}

onSuggest method for City field :

public List onCitySuggest(String searchCityName) {
ArrayList<SelectItem> selectItems = new ArrayList<SelectItem>();

System.out.println(searchCityName);
//get access to the binding context and binding container at runtime
BindingContext bctx = BindingContext.getCurrent();
BindingContainer bindings = bctx.getCurrentBindingsEntry();
//set the bind variable value that is used to filter the View Object
//query of the suggest list. The View Object instance has a View
//Criteria assigned
OperationBinding setVariable = (OperationBinding) bindings.get("setBind_CityName");
setVariable.getParamsMap().put("value", searchCityName);
setVariable.execute();
//the data in the suggest list is queried by a tree binding.
JUCtrlHierBinding hierBinding = (JUCtrlHierBinding) bindings.get("CityView1");


//re-query the list based on the new bind variable values
hierBinding.executeQuery();

//The rangeSet, the list of queries entries, is of type
//JUCtrlValueBndingRef.
List<JUCtrlValueBindingRef> displayDataList = hierBinding.getRangeSet();

for (JUCtrlValueBindingRef displayData : displayDataList){
Row rw = displayData.getRow();
//populate the SelectItem list
selectItems.add(new SelectItem(
(Integer)rw.getAttribute("Id"),
(String)rw.getAttribute("Name")));
}

return selectItems;
}

Once after this, we could bind this to the af:autoSuggestBehavior's suggestedItems property

<af:inputText value="#{bindings.CountryId.inputValue}" label="#{bindings.CountryId.hints.label}"
columns="#{bindings.CountryId.hints.displayWidth}"
autoSubmit="true"
shortDesc="#{bindings.CountryId.hints.tooltip}" id="it3">
<af:autoSuggestBehavior suggestedItems="#{viewScope.AutoSuggestBean.onCountrySuggest}"/>
</af:inputText>
<af:inputText value="#{bindings.StateId.inputValue}" label="#{bindings.StateId.hints.label}"
columns="#{bindings.StateId.hints.displayWidth}"
autoSubmit="true" partialTriggers="it3"
shortDesc="#{bindings.StateId.hints.tooltip}" id="it4">
<af:autoSuggestBehavior suggestedItems="#{viewScope.AutoSuggestBean.onStateSuggest}"/>
</af:inputText>
<af:inputText value="#{bindings.CityId.inputValue}" label="#{bindings.CityId.hints.label}"
columns="#{bindings.CityId.hints.displayWidth}"
autoSubmit="true" partialTriggers="it4"
shortDesc="#{bindings.CityId.hints.tooltip}" id="it5">
<af:autoSuggestBehavior suggestedItems="#{viewScope.AutoSuggestBean.onCitySuggest}"/>
</af:inputText>

Finally, our output would be

output1.jpg

output2.jpg

output3.jpg

Monday Jan 14, 2013

Display Holiday Name in af:calendar

When using af:calendar component in the realtime applications, there is a frequent requirement to display the holdays in it. In this article, we will see how to achieve that.

After setting the environment, we would be expecting the output as shown in the below image.

finalOutput.jpg

Assuming we already have a page with calendar, the individual date level customizations can be done by using DateCustomizer.

For this, we would create a custom class that extends DateCustomizer class.

public class MyDateCustomizer extends DateCustomizer{
public String format(Date date, String key, Locale locale, TimeZone tz)
{
// For illustrative purpose
// Hashmap holding the holiday list
HashMap holidays = new HashMap();
holidays.put(new Date("25-Dec-2012"), "Christmas");
holidays.put(new Date("01-Jan-2013"), "New Year");

if ("af|calendar::month-grid-cell-header-misc".equals(key))
{
return holidays.get(date)!=null?holidays.get(date).toString():null;

}

return null;

}

}

As per the tag doc, following keys are passed to the format method.

  • "af|calendar::day-header-row"
  • "af|calendar::list-day-of-month-link"Year's Day".
  • "af|calendar::list-day-of-week-column"
  • "af|calendar::month-grid-cell-header-day-link"
  • "af|calendar::month-grid-cell-header-misc"
  • "af|calendar::week-header-day-link"

So, in the above code, we return the actual holiday name for the "af|calendar::month-grid-cell-header-misc" key.

In order to use the new DateCustomizer in our calendar, we create an instance of it in the backing bean and bind it to the calendar's dateCustomizer property.

Bean Code :

public class CalendarBean {
private MyDateCustomizer holidays = new MyDateCustomizer(); ;

public CalendarBean() {
}


public void setHolidays(MyDateCustomizer holidays) {
this.holidays = holidays;
}

public MyDateCustomizer getHolidays() {
return holidays;
}
}


Page Source :

<af:calendar id="c1" dateCustomizer="#{viewScope.CalendarBean.holidays}"/>

If needed, the text can be styled to be displayed in different color (as shown in the image at the top). For this, we can have a styleclass in the css

af|calendar::month-grid-cell-header-misc
{
background-color: Yellow;
}

Thursday Aug 02, 2012

Exposing Multiple Methods under One Resource In a Single URL Data Control

Previously, a URL Data Control could be associated with only one HTTP Method. So  if the developer had to expose multiple operations under  a single Resource, he/she had to create one DC for each operation.  Now since Jdeveloper  supports such a functionality from 11.1.2.2.0 version, multiple operations can be included in a single DC. This blog explains how to include all the operations exposed by a REST Service Resource in a single Data Control. We will use following REST Service to create URL Data Control.

import java.util.ArrayList;
import java.util.Iterator;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

@Path("project1")
public class EmployeeService {
    private ArrayList<Employee> EmpList = new ArrayList<Employee>();

    public EmployeeService() {
        EmpList.add(new Employee("Adam", 1001, 35, "ADF"));
        EmpList.add(new Employee("Bob", 1002, 42, "HR"));
        EmpList.add(new Employee("George", 1003, 54, "Apps"));

    }

    @GET
    @Produces("application/xml")
    public ArrayList<Employee> getEmpList() {
        return EmpList;
    }

    @POST
    @Consumes("application/xml")
    @Produces("application/xml")
    public ArrayList<Employee> addEmployee(Employee emp) {
        EmpList.add(emp);
        return EmpList;
    }

    @DELETE
    @Consumes("text/plain")
    public ArrayList<Employee> deleteEmployee(@QueryParam("name")
                                              String name) {
        Iterator it = EmpList.iterator();
        while (it.hasNext()) {
            Employee tmp = (Employee) it.next();
            if (tmp.getName().equalsIgnoreCase(name))
                EmpList.remove(tmp);

        }
        return EmpList;
    }

}
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Employee {
    String name;
    int id;
    int age;
    String dept;
    
    public Employee() {
        super();
    }

    Employee(String name, int id, int age, String dept) {
        this.name= name;
        this.id=id;
        this.age=age;
        this.dept=dept;
        
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setDept(String dept) {
        this.dept = dept;
    }

    public String getDept() {
        return dept;
    }
}

Run the REST Service before commencing with Data Control. 

To begin with, create ADF Fusion Application. In Model project invoke create DC Wizard. Name your Data Control RESTDC and click on Next to create URL Connection.



In the next step, give a name to the URL connection, for example URLConn and specify localhost address in the URL Endpoint field.

Also, choose GET as HTTP Method and supply remaining part of REST Service URL in the source field.



In Step 4, choose XML as the data format from the drop down menu. Click Next. XSD will be auto generated by the jdeveloper. In step 5, click on Test URL Connection to check if the connection has been successfully generated.



Click on finish to complete the wizard. The Data Control for GET method in the REST Service has been created successfully and can be viewed under Data Controls Panel.




Now, we will add remaining HTTP Methods in the service to RESTDC. Invoke Create URL DC Wizard in the Model project. Give RESTDC (same name as the previous DC) in the Name field and choose URLConn from the drop- down. Select POST as the HTTP Method and supply the path to POST Operation in source field.



In the next step, specify the XSD which specifies the structure of the request content. Click Next.Choose XML Data format from the drop-down and complete the wizard. You can see another operation added to the same Data Control in the DC Panel.




Similarly invoke Create URL DC Wizard to add DELETE Method to RESTDC.



In step 3, you will be asked to enter a value to test whether URL and source are valid. Enter  "abc" in the values field against param.



Choose XML as Data format and complete the wizard.

Now the Data Controls Panel has all three operations GET, POST and DELETE which are in the resource under a single URL DC.



The names of these methods (loadData, loadData1 etc) can be changed from DataControls.dcx file.


Wednesday Jul 18, 2012

How to access published ADFdi enabled excel workbook from ADF Library jar

We generally tend to create multiple applications and re-use them in one main application by adding them as ADF Library jars.Applications with ADF Desktop Integration support added to ViewController can also be re-used.

This blog is about how to access workbooks in ADF Library jar from a different application(which consumes ADF Library Jar).

Assumption : Let's assume that a simple ADF application(TestApp) is created with Dept and Emp as entities and added as Master Form Detail Table in jspx.Also an ADFdi enabled excel workbook is created to simulate DeptView and EmpView as Master Form Detail Table.Publish the workbook(PBook1.xlsx) and store it in ViewController/public_html/excel/ folder and check for the working of published workbook by re-deploying the application.

Now,let's proceed to the steps to be done before deploying ViewController as ADF Library jar.

Open web.xml in ViewController->Web Content->WEB-INF of TestApp, in Filters tab, check for adfBindings and adfdiExcelDownload filters. If not present add by referring this ADFdi guide.

Create a new deployment profile for ViewController by right-click ViewController and Deploy->New Deployment profile. Select ADF Library Jar File as Profile Type and give a profile name like TestAppLibJar

Create a new custom application(ClientApp) with ADF ViewController as project.

Create a FileSystemConnection to import the deployed library jar of TestApp as below:
In Resource Pallete, open New File System Connection, give Connection Name as TestAppConn and for DirectoryPath browse and select the deploy folder inside ViewController of TestApp application.

FSConnection

Select ViewController of ClientApp, expand the TestAppConn(File System Connection) created, select TestAppLibJar, right-click and Add to Project. In Confirmation dialog, click on Add Library,

AddJar

In ViewController add ADF Desktop Integration support by right-click ViewController->Project Properties->Features, click on + sign and move ADF Desktop Integration from Available to Selected.

ADFdiScope

Add ADF Library Web Application Support into ViewController of ClientApp by right-click ViewController->Project Properties->ADF View, select Enable ADF Library WebApp Support checkbox.

ADFLibWebAppSup

Open web.xml and check for the correct ordering of ADFLibraryFilter and adfdiExcelDownload filter. Make sure adfdiExcelDownload filter is above ADFLibraryFilter.

webxml_clientApp

Add Initialization Parameter for ADFLibraryFilter with Name 'include-extension-list' and Value 'png,jpg,jpeg,gif,js,css,htm,html,xlsx' as shown in above pic.

Now we will create a jspx page and add Go link with Text 'Download Excel' and Destination '/excel/PBook1.xlsx'

DloadPage

Run the jspx page and click on Download Excel link. Click on Open, and give yes in Login dialog. There we go, here is our published workbook.

FinalPBook

Tuesday Jul 17, 2012

Interpret af:query's queryEvent and display popup to end user using QueryListener

Found an interesting question on OTN. Based on the question, wired a usecase to try out.

Usecase : Show a warning to user when they try to search the records (af:query component), without specifying a criteria / a wild card "%". I.e, when the user tries to query the entire table, show a warning that querying all the records would take some time.

There are three phases in implementing this usecase.

1. Interpret the query event and get the query criteria.
2. Show the popup.
3. Process the interpreted query based on the outcome of the popup.

Before proceeding with the implementation, we'll create a page for assumption.
a. Page contains a af:query component with a resultant table / read-only table.
b. Has a popup to be shown to the end user.
c. Bound to a bean.

We'll now implement it phase by phase.

First of all, we'll create couple of attributes in the bean and generate accessors to them.
    private RichTable empTable;
    private boolean warnUser=true;
   // Set the default queryListener property value of the af:query component
   // for mexpr.
   private String mexpr = "#{bindings.ImplicitViewCriteriaQuery.processQuery}"; 
    private QueryEvent qEvt;

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

    public RichTable getEmpTable() {
        return empTable;
    }



Now, we need to trap the query event of the af:query component to perform the desired task. We can add a queryListener and bind it to the af:query component

  public void processQuery(QueryEvent queryEvent) {
        // store the queryEvent in a bean attribute, to be used in another method.
        qEvt = queryEvent;
        // Reset the flag. This flag would be used to check if the system has to 
        //raise the popup or not
        warnUser=false;
        DCBindingContainer bc =   
        (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();   
         // Get the view criteria that would be applied.
         // findExecutableBinding method takes two parameters.
         // id of the searchRegion executable binding
         // criteria for the searchRegion executable binding
         // Ex : <searchRegion Criteria="__ImplicitViewCriteria__"      
         // Customizer="oracle.jbo.uicli.binding.JUSearchBindingCustomizer"
         //         Binds="EmpView1Iterator" id="ImplicitViewCriteriaQuery"/>
       


 ViewCriteria vc = 
JUSearchBindingCustomizer.getViewCriteria((DCBindingContainer)bc.findExecutableBinding("ImplicitViewCriteriaQuery"),"__ImplicitViewCriteria__");


        ViewCriteriaRow vcr = (ViewCriteriaRow)vc.get(0);
        // Some logic to set the flag. Here, checking if the Ename attribute has
        // no value specified / used a wildcard expression ("%").
        for(int i=0;i<vcr.getAttributeNames().length;i++) {
           
 if(vcr.getAttributeNames()[i] == "Ename" &&  
("%".equals(vcr.getAttributeValues()[i]) || 
vcr.getAttributeValues()[i]==null)) 
                warnUser=true;
        }
 
        if(warnUser) 
            showPopup();
        else 
            executeQuery();
 
    }



showPopup and executeQuery are custom methods to show the popup and to process the query respectively.

       public void showPopup(){
        UIViewRoot root = FacesContext.getCurrentInstance().getViewRoot();
        RichPopup popup = (RichPopup) root.findComponent("p1");
        RichPopup.PopupHints hints = new RichPopup.PopupHints();
        popup.show(hints);
    }

  // This method invokes the method expression used by af:query component programatically
    public void executeQuery(){
        processMethodExpression(mexpr, new Object[] {qEvt}, new Class[] {QueryEvent.class}); 
        AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance(); 
        adfFacesContext.addPartialTarget(empTable); 
 
    }

    private Object processMethodExpression(String methodExpression, Object[] parameters, Class[] expectedParamTypes) { 
        FacesContext fctx = FacesContext.getCurrentInstance(); 
        ELContext elctx = fctx.getELContext(); 
        Application app = fctx.getApplication(); 
        ExpressionFactory exprFactory = app.getExpressionFactory(); 
       
 MethodExpression methodExpr = exprFactory.createMethodExpression(elctx,
 methodExpression, Object.class, expectedParamTypes); 
        return methodExpr.invoke(elctx, parameters); 
        }


Now, we need to bind the custom querListener created above to the af:query component

 <af:query id="qryId2" headerText="Search" disclosed="true"
   value="#{bindings.ImplicitViewCriteriaQuery.queryDescriptor}"
   model="#{bindings.ImplicitViewCriteriaQuery.queryModel}"
   queryListener="#{viewScope.QueryBean.processQuery}".....



We are almost there. Now, when we run the page and query for the records by keeping Ename as null (in the query panel), we would get the popup.

Final step is to handle the user action on the popup and then proceed executing the query / to stop it.

For this, we'll create a dialog listener and bind it to popup.

 public void onDialog(DialogEvent dialogEvent) {
        Outcome o = dialogEvent.getOutcome();
        if(o == Outcome.yes) 
            executeQuery();
 
    }

                <af:popup childCreation="deferred" autoCancel="disabled" id="p1">
                    <af:dialog id="d2" type="yesNo" title="Are you sure?"
                               dialogListener="#{viewScope.QueryBean.onDialog}">
                       
 <af:outputText value="It would be time consuming to query for all 
records. Are you sure you want to continue?" id="ot9"/>
                        <f:facet name="buttonBar"/>
                    </af:dialog>
                </af:popup>



Here is how the runtime would be.

step1.jpg



Enter % for Ename and hit the Search button

" class="mt-enclosure mt-enclosure-image">step2.jpg



Popup with a warning message displayed

step3.jpg


Clicking on Yes / No on the popup performs respective task (perform query / cancel query).

step4.jpg



Thursday Jun 21, 2012

An Epic Question "How to call a method when the page loads"

Quite often, there comes a question in OTN, with different subjects, all meaning "How to call a method when my ADF page loads?". More often, people tend to take the approach of ADF Phase Listener by overriding before/afterPhase methods.

In this blog, we will go through different options in achieving it.

1. Method Call Activity as default activity in Taskflow :

If the application is built with taskflows, then this is the best suited approach to take.

1.a. Calling a Data Control Method :

To call a Data Control method (ex: A method in AMImpl exposed as client interface), simply Drag and Drop the method as Default Method Call Activity, then draw a control flow case from the method to your page.


DCMethodTF.jpg

Once after this, drop the taskflow as region in main page. When we run the main page, the Method Call Activity would be called first, and then the page will be rendered.

1.b. Calling a Method in Backing Bean:

To call a method in the backing bean before pageload, we can follow the similar approach as above. Instead of binding the Method Call Activity to an action/method binding in pagedef, we bind to the method. Insert a Method Call Activity (and make it as default) from the Component Palette. Double click on to select a method to bind.

BBMethodTF.jpg



This approach can also be used, to perform some action in backing bean along with calling a method Data Control (just need to add bindings code in backing bean to execute DC method).


2. Using invokeAction Executable :

If the application is built with pages and no taskflows are involved, then this option can be taken into consideration.

In the page definition of the page, add an invokeAction Executable and bind it to the method needed to be executed.

DCMethodInvokeAction.jpg


3.
Using combination of Server and Client Listeners :

If the page does not have any page definition, then to call a method in backing bean, this approach can be taken. In this, a serverListener would be added at the document level, which would be calling the method in backing bean. Along with this, a clientListener would be added with "load" type (i.e will be triggered when the page loads), which would queue a serverEvent to trigger the method.

BBMethodListener.jpg



4. Using Page Phase Listener :

This should be the last resort. Care should be taken when using this approach since the Phase Listener would be called for each request sent by the client.

Zeeshan Baig's blog covers this scenario.

Monday May 21, 2012

Executing Put operation of REST service programatically from ADF App

In quite some cases, we would like to call the PUT method on a REST service by constructing the parameters during runtime and pass it on. In this article, we would go through how to deal with such cases when building an ADF Application.

Refer this tutorial for introduction to REST service in where, GET and DELETE methods are explained. In this sample, we'll see how to implement PUT operation using a HashMap. In fact, as like in the above tutorial, we can directly execute the PUT method as well. This article is mainly concentrated on how to construct the parameters dynamically at runtime.

First let us create a simple POJO to hold employee records.

package project1;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement

public class Emp {
    public Emp() {
        super();
    }
    
    private String name;
    private int id;
    private int exp;
    private int salary;

    public Emp(String name, int id, int exp, int salary) {
        super();
        this.name = name;
        this.id = id;
        this.exp = exp;
        this.salary = salary;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setExp(int exp) {
        this.exp = exp;
    }

    public int getExp() {
        return exp;
    }

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

    public int getSalary() {
        return salary;
    }
    
    public String toString() {
        String empXml = "<name>"+getName()+"</name>"+"\n"+
                       "<id>"+getId()+"</id>"+"\n"+
                        "<experience>"+getExp()+"</experience>"+"\n"+
                       "<salary>"+getSalary()+"</salary>";
        return empXml;                  
    }
}
Then, create a REST service using the for the Employee.

package project1;

import javax.ws.rs.Path;
import java.util.*;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;

import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

@Path("/test")
public class Service {
    
   static ArrayList<Emp> emp = new ArrayList<Emp>();
  
    public Service() {
        super();
     }
    
    @GET
    @Produces("application/xml")
    public  ArrayList<Emp> getEmps() {
       
        return emp;
        
    }
    
    @DELETE
    public void deleteEmp(@QueryParam("id") int id) {
       emp.remove(getObj(id));
    }
    
    @PUT
    @Consumes("application/xml")
    public void addEmp( Emp e) {
        emp.add(e);
    }
    
    @PUT
    @Path("defaultEmp")
    public Response addEmp() {
        emp.add(new Emp("abc",1,5,10000));
        emp.add(new Emp("xyz",2,7,15000));
        emp.add(new Emp("lmn",3,5,8000));
        return Response.ok().build();
            
    }
    
    @POST
    public void upadteEmp(@QueryParam("id")int id,Emp e) {
      deleteEmp(id);
      addEmp(e);
    }
    
    public Emp getObj(int id) {
        Iterator i = emp.iterator();
        while(i.hasNext()){
        Emp emp = (Emp)i.next();
        if((emp.getId())==id)
        {
            System.out.println(emp.getName());
            return emp;
        }
        } 
        return null;
    }
}

We'll come to the UI part now.

After creating a Fusion Web Application from JDeveloper, create a new URL Data Control for the REST service created above (for GET and PUT Operations).

DataControls.dcx looks like below

DataControls.jpg



Now, our aim is to have a UI, from where we can enter the employee details. Once after having the data, construct parameter object and execute loadData (PUT) method.

This is done by having 4 input texts and bind them to attributes in the backing bean. Drag and Drop the loadData method from RestPut DataControl (and do not specify a value for the parameter).



And the code snippet of the jspx page

<af:panelFormLayout id="pfl1">
    <f:facet name="footer">
        <af:commandButton text="Put"
          disabled="#{!bindings.loadData.enabled}" id="cb1"
          actionListener="#{pageFlowScope.RestBean.performPut}"/>
    </f:facet>
    <af:inputText label="Id" id="it1" 
                  value="#{pageFlowScope.RestBean.id}" autoSubmit="true"/>
    <af:inputText label="Name" id="it2" 
                  autoSubmit="true" value="#{pageFlowScope.RestBean.name}"/>
    <af:inputText label="Exp" id="it3" 
                  value="#{pageFlowScope.RestBean.exp}" autoSubmit="true"/>
    <af:inputText label="Sal" id="it4" 
                  autoSubmit="true" value="#{pageFlowScope.RestBean.sal}"/>
</af:panelFormLayout>

In the backing bean (RestBean), we have 4 attributes with accessors that are mapped to the Text Items.

    private Number id,sal,exp;
    private String name;


    public void setId(Number id) {
        this.id = id;
    }

    public Number getId() {
        return id;
    }

    public void setSal(Number sal) {
        this.sal = sal;
    }

    public Number getSal() {
        return sal;
    }

    public void setExp(Number exp) {
        this.exp = exp;
    }

    public Number getExp() {
        return exp;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }


Now, we'll add an actionListener code in the backing bean, in that, we can construct a Map with the required values and then execute the method by passing Map to it.

    public void performPut(ActionEvent actionEvent) {
        BindingContainer bindings = getBindings();
        Map putParamMap = new HashMap();
        putParamMap.put("id", getId());
        putParamMap.put("name", getName());
        putParamMap.put("exp", getExp());
        putParamMap.put("sal", getSal());

        OperationBinding operationBinding = bindings.getOperationBinding("loadData");
        operationBinding.getParamsMap().put("emp",putParamMap);
        Object result = operationBinding.execute();
        if (!operationBinding.getErrors().isEmpty()) {
            System.out.println("Error processing put operation..");    
        }
    }

    public BindingContainer getBindings() {
        return BindingContext.getCurrent().getCurrentBindingsEntry();
    }
In the above code, we find the loadData method from the DataBindings entry, create a Map with all the required attributes to create an Employee record, get the Parameter list for the method and pass the Map to method as parameter to execute it.


Thursday May 03, 2012

Construct ADF Master Detail Tree using EJB Datacontrol Objects programatically

This article describes the creation of master detail Tree using EJB Datacontrol Objects.

Use Case Description

Construct ADF Tree using Employees & Departments objects.It is straight forward to build a tree when foreign key relationship exists between the objects. For cases where no foreign key relationships exist, we can use this technique to programatically construct the tree in the view layer.

Implementation steps

Let us suppose that we have created Java EE Web Application with Entities from Employees & Departments table .Also add 'ADF Faces Components 11' under ViewController->project properties-> JSP Tag Libraries.

Then we will create Stateless Session Bean and and generate data control for the Stateless Session Bean.

Create Tree.jspx page .Goto its page definition overview editor & create table Binding for departmentsFindAll

Similarly create table binding for employeesFindAll.

Now we will create 2 Beans:

TreeItem.java
  - Defines Tree structure
DisplayTree.java
 Managed bean - Programatically construct tree node items

Copy this code in Tree.jspx source page:

<af:tree var="node" value="#{DisplayTree.model}">
   <f:facet name="nodeStamp">
    <af:outputText id="l1" value="#{node.text}"/>
    </f:facet>
</af:tree>

Run the page & it displays Departments->Employees Tree .


Wednesday Apr 25, 2012

EJB DataControl - programmatically construct Master-Detail hierarchy

Sometimes, in EJB data control it is necessary to construct Master-Detail relationships across different levels pro-grammatically. One of the most common use cases is construct the master-detail relation based on database tables where tables doesn't have foreign key relationship. So in this article, I'm trying to construct master-detail hierarchy pro-grammatically by taking simple custom_dept, custom_emp tables.

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

Note:- In Bean Data Control, Master-Detail hierarchy can be constructed as a same way but  java bean classes to be created first and then pro-grammatically populate the data in session facade.

Model Diagram:

BMD-Model.png


Note:- Here entities doesn't have any foreign key relationship. DB script for creating the tables and inserting the data required for this application is in application/etc folder.

Implementation Steps

Create Java EE Web Application with entities based on custom_dept and custom_emp tables, then create a session bean and data control for the session bean. Open cusotm_dept entity and create a transient variable called "empCollection" and add the below code.

@Transient
private Collection<CustomEmp> empCollection = new ArrayList();

public void setEmpCollection(Collection<CustomEmp> empCollection) {
	this.empCollection = empCollection;
}

public Collection<CustomEmp> getEmpCollection() {
	return empCollection;
}
Open session facade, add the below code to the session facade and expose the masterDetailFindAll() method in local/remote interface and generate a data control for that.

Note:- Here in the below code "em" is a EntityManager.
public List<CustomDept> masterDetailFindAll() {
String deptQueryString = "select * from custom_dept";
System.out.println(deptQueryString);
Query deptSearchQuery = em.createNativeQuery(deptQueryString, "deptQuery");
List deptResultList = deptSearchQuery.getResultList();
Iterator deptListIterator = deptResultList.iterator();
List<CustomDept> deptList = new ArrayList();
while (deptListIterator.hasNext()) {
	Object deptCol[] = (Object[])deptListIterator.next();
	CustomDept dept = new CustomDept();
	BigDecimal departmentId = (BigDecimal)deptCol[0];
	dept.setDepartmentId(departmentId);
	dept.setDepartmentName((String)deptCol[1]);
	dept.setLocationId((BigDecimal)deptCol[2]);
	String empQueryString =
		"select * from custom_emp emp, custom_dept dept where emp.department_id = dept.department_id and dept.department_id = " +
		departmentId;
	Query empSearchQuery = em.createNativeQuery(empQueryString, "empQuery");
	List empResultList = empSearchQuery.getResultList();
	Iterator empListIterator = empResultList.iterator();
	List<CustomEmp> empList = new ArrayList();
	while (empListIterator.hasNext()) {
		Object empCol[] = (Object[])empListIterator.next();
		CustomEmp emp = new CustomEmp();
		emp.setEmployeeId((BigDecimal)empCol[0]);
		emp.setFirstName((String)empCol[1]);
		emp.setLastName((String)empCol[2]);
		emp.setEmail((String)empCol[3]);
		emp.setJobId((String)empCol[4]);
		emp.setDepartmentId((BigDecimal)empCol[5]);
		empList.add(emp);
	}
	dept.setEmpCollection(empList);
	deptList.add(dept);
 }
  return deptList;
}

In the ViewController create index.jspx page, from data control palette drag and drop masterDetailFindAll()->CustomDept->empCollection->Master-Detail as ADF Master Form, Detail Table.

Run the index.jspx page, now we can traverse through Master-Detail records.

BMD-result.png

Thursday Apr 19, 2012

Display Lookup values from related ejb entities using JOIN FETCH

JOIN FETCH - The purpose of JOIN FETCH is to fetch the related objects from the database in a single query. So in this article, I'm trying to explain how can we use jpql JOIN FETCH and expose those attributes to EJB data control layer.

Take a scenario, where we need to build ADF tree based on departments,employees and location tables. While displaying the tree, root node should display departmentName along with city attribute which is stored in related object Location table.

Model Diagram:

EJBJFA-Model.png


In BC4J, this scenario can be implemented using Entity Objects facility provided in View Object layer. These entity objects are used by the view object for accessing the related objects attributes and will be exposed in data control layer automatically.

Same behavior can be implemented in EJB using JOIN FETCH. Using this query improves the efficiency of iteration over the result Departments objects because it eliminates the need for retrieving the associated Location objects separately.So in single query related objects attributes is also fetched from the database.

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

Implementation Steps

Create Java EE Web Application with entities based on Departments, Employees and Location table, then create a session bean and data control for the session bean.

Open the departments entity and alter the named query as below.

@NamedQuery(name = "Departments.findAll",
                             query = "select o from Departments o join fetch o.locations")

Note:- The query above returns Departments instances and guarantees that the locations attributes will already be fetched in the returned instances.

Now we need to expose the location attribute values to data control, create a Transient variable called "city" and add the below code in department entity.

@Transient
 private String city;
	
public String getCity() {
  return this.locations.getCity();
}

Note: Can create sample java client to check whether the city attribute value is coming in departments instance before proceeding to data control.

In the ViewController create index.jspx page, from data control palette drop departmentsFindAll->Tree as ADF Tree and in edit tree bindings select the attributes as shown in below image.

EJBJFA-DeptBinding.png


Run the index.jspx page. Now notice root node will display departmentName along with city attribute value also.

EJBJFA-output.png

Thursday Apr 05, 2012

EJB Named Criteria - Apply bind variable in Backingbean

EJB Named criteria are predefined and reusable where-clause definitions that are dynamically applied to a ViewObject query. Here we often use to filter the ViewObject SQL statement query based on Where Clause conditions.

Take a scenario where we need to filter the SQL statements query based on Where Clause conditions, instead of playing with SQL statements use the EJB Named Criteria which is supported by default in ADF and set the Bind Variable parameter at run time.

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

Implementation Steps

Create Java EE Web Application with entity based on Employees table, then create a session bean and data control for the session bean.

Open the DataControls.dcx file and create sparse xml for as shown below.

ncbva-dcx.png


In sparse xml navigate to Named criteria tab -> Bind Variable section, create binding variable deptId.

ncbva-bindvariable.png


Now create a named criteria and map the query attributes to the bind variable.

ncbva-namedcriteria.png


In the ViewController create index.jspx page, from data control palette drop employeesFindAll->Named Criteria->EmployeesCriteria->Table as ADF Read-Only Filtered Table and create the backingBean as "IndexBean".

Open the index.jspx page and remove the "filterModel" binding from the table, add <af:inputText />, command button and bind them to backingBean. For command button create the actionListener as "applyEmpCriteria" and add below code to the file.

public void applyEmpCriteria(ActionEvent actionEvent) {
   DCIteratorBinding dc = (DCIteratorBinding)evaluteEL("#{bindings.employeesFindAllIterator}");
   ViewObject vo = dc.getViewObject();
   vo.applyViewCriteria(vo.getViewCriteriaManager().getViewCriteria("EmployeesCriteria"));
   vo.ensureVariableManager().setVariableValue("deptId", this.getDeptId().getValue());
   vo.executeQuery();
}

/**
 * Programmtic evaluation of EL
 *
 * @param el EL to evalaute
 * @return Result of the evalutaion
 */
public Object evaluteEL(String el) {
	FacesContext fctx = FacesContext.getCurrentInstance();
	ELContext elContext = fctx.getELContext();
	Application app = fctx.getApplication();
	ExpressionFactory expFactory = app.getExpressionFactory();
	ValueExpression valExp = expFactory.createValueExpression(elContext, el, Object.class);
	return valExp.getValue(elContext);
}
Run the index.jspx page, enter departmentId value as 90 and click in ApplyEmpCriteria button. Now the bind variable for the Named criteria will be applied at runtime in the backing bean and it will re-execute ViewObject query to filter based on where clause condition.

ncbva-filteredresult.png

Monday Mar 26, 2012

Implement Tree/Details With Taskflow Regions Using EJB

This article describes on Display Tree/Details using taskflow regions.

Use Case Description

Let us take scenario where we need to display Tree/Details, left region contains category hierarchy with items listed in a tree structure (ex:- Region-Countries-Locations-Departments in tree format) and right region contains the Employees list.

In detail, Here User may drills down through categories using a tree until Employees are listed. Clicking the tree node name displays Employee list in the adjacent pane related to particular tree node.

Implementation Steps

The script for creating the tables and inserting the data required for this application

CreateSchema.sql


Lets create a Java EE Web Application with Entities based on Regions, Countries, Locations, Departments and Employees table. Create a Stateless Session Bean and data control for the Stateless Session Bean. Add the below code to the session bean and expose the method in local/remote interface and generate a data control for that.

Note:- Here in the below code "em" is a EntityManager.

public List<Employees> empFilteredByTreeNode(String treeNodeType, String paramValue) {
        String queryString = null;
        try {
            if (treeNodeType == "null") {
                queryString = "select * from Employees emp ORDER BY emp.employee_id ASC";
            } else if (Pattern.matches("[a-zA-Z]+[_]+[a-zA-Z]+[_]+[[0-9]+]+", treeNodeType)) {
                queryString = "select * from employees emp INNER JOIN departments dept\n" +
                        "ON emp.department_id = dept.department_id JOIN locations loc\n" +
                        "ON dept.location_id = loc.location_id JOIN countries cont\n" +
                        "ON loc.country_id = cont.country_id JOIN regions reg\n" +
                        "ON cont.region_id = reg.region_id and reg.region_name = '" + paramValue +
                        "' ORDER BY emp.employee_id ASC";
            } else if (treeNodeType.contains("regionsFindAll_bc_countriesList_1")) {
                queryString = "select * from employees emp INNER JOIN departments dept \n" +
                        "ON emp.department_id = dept.department_id JOIN locations loc \n" +
                        "ON dept.location_id = loc.location_id JOIN countries cont \n" +
                        "ON loc.country_id = cont.country_id and cont.country_name = '" + paramValue +
                        "' ORDER BY emp.employee_id ASC";
            } else if (treeNodeType.contains("regionsFindAll_bc_locationsList_1")) {
                queryString =
                        "select * from employees emp INNER JOIN departments dept ON emp.department_id = dept.department_id JOIN locations loc ON dept.location_id = loc.location_id and loc.city = '" +
                        paramValue + "' ORDER BY emp.employee_id ASC";

            } else if (treeNodeType.trim().contains("regionsFindAll_bc_departmentsList_1")) {
                queryString =
                        "select * from Employees emp INNER JOIN Departments dept ON emp.DEPARTMENT_ID = dept.DEPARTMENT_ID and dept.DEPARTMENT_NAME = '" +
                        paramValue + "'";
            }
        } catch (NullPointerException e) {
            System.out.println(e.getMessage());
        }   
        return em.createNativeQuery(queryString, Employees.class).getResultList();
    }

In the ViewController project, create two ADF taskflow with page Fragments and name them as FirstTaskflow and SecondTaskflow respectively. Open FirstTaskflow,from component palette drop view(Page Fragment) name it as TreeList.jsff. Open SeconfTaskflow, from component palette drop view(Page Fragment) name it as EmpList.jsff and create two paramters in its overview parameters tab as shown in below image.
TaskflowParameters.png

Open TreeList.jsff , from data control palette drop regionsFindAll->Tree as ADF Tree. In Edit Tree Binding dialog, for Tree Level Rules select the display attributes as follows:-
model.Regions - regionName
model.Countries - countryName
model.Locations - city
model.Departments - departmentName

EditTreeBinding.png


In structure panel, click on af:Tree - t1 and select selectionListener with edit property. Create a "TreeBean" managed bean with scope as "session" as shown in below Image.

TreeBean.png


Create new method as getTreeNodeSelectedValue and click ok.

getTreeNodeSelectedValue.png


Open TreeBean managed bean and add the below code:

private String treeNodeType;
private String paramValue;

public void getTreeNodeSelectedValue(SelectionEvent selectionEvent) {
        RichTree tree = (RichTree)selectionEvent.getSource();
        RowKeySet addedSet = selectionEvent.getAddedSet();
        Iterator i = addedSet.iterator();
        TreeModel model = (TreeModel)tree.getValue();
        model.setRowKey(i.next());
        JUCtrlHierNodeBinding node = (JUCtrlHierNodeBinding)tree.getRowData();
        //oracle.jbo.Row
        Row rw = node.getRow();
        Object selectedTreeNode = node.getAttribute(0);

        Object treeListType = node.getBindings();
        String treeNodeType = treeListType.toString();

        this.setParamValue(selectedTreeNode.toString());
        this.setTreeNodeType(treeNodeType);
    }

    public void setTreeNodeType(String treeNodeType) {
        this.treeNodeType = treeNodeType;
    }

    public String getTreeNodeType() {
        return treeNodeType;
    }

    public void setParamValue(String paramValue) {
        this.paramValue = paramValue;
    }

    public String getParamValue() {
        return paramValue;
    }<br />
Open EmpList.jsff , from data control palette drop empFilteredByTreeNode->Employees->Table as ADF Read-only Table. After selecting the  Employees result set, in Edit Action Binding dialog window pass the pageFlowScope parameters as shown in below Image.

EmpEditActionBinding.png


In empList.jsff page, click Binding tab and click on Create Executable binding and select Invoke action and follow as shown in below image.

executeEmpFiltered.png



Edit executeEmpFiltered invoke action properties and set the Refresh to ifNeeded, So when ever the page needs the method will be executed.

executeEmpFilteredInvoke.png


Create Main.jspx page with page template as Oracle Three Column Layout. Drop FirstTaskflow as Region in start facet and drop SecondTaskflow as Region in center facet, Edit task Flow Binding dialog window pass the Input Paramters as shown in below Image.

EditTaskFlowBinding.png


Run the Main.jspx, tree will be displayed in left region and emp details will displyaed on the right region. Click on the Americas in tree node, all emp related to the Americas related will be displayed.

ResultNew.png


Click on Americas->United States of America->South San Francisco->Accounting, only employee belongs to the Accounting department will be displayed.

SelectedResult.png

Tuesday Mar 20, 2012

Achieve Named Criteria with multiple tables in EJB Data control

In EJB create a named criteria using sparse xml and in named criteria wizard, only attributes related to the that particular entities will be displayed.  So here we can filter results only on particular entity bean.

Take a scenario where we need to create Named Criteria based on multiple tables using EJB. In BC4J we can achieve this by creating view object based on multiple tables. So in this article, we will try to achieve named criteria based on multiple tables using EJB.

Implementation Steps

Create Java EE Web Application with entity based on Departments and Employees, then create a session bean and data control for the session bean.

Create a Java Bean, name as CustomBean and add below code to the file. Here in java bean from both Departments and Employees tables three fields are taken.

public class CustomBean {
    private BigDecimal departmentId;
    private String departmentName;
    private BigDecimal locationId;
    private BigDecimal employeeId;
    private String firstName;
    private String lastName;

    public CustomBean() {
      super();
    }

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

    public BigDecimal getDepartmentId() {
        return departmentId;
    }

    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }

    public String getDepartmentName() {
        return departmentName;
    }

    public void setLocationId(BigDecimal locationId) {
        this.locationId = locationId;
    }

    public BigDecimal getLocationId() {
        return locationId;
    }

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

    public BigDecimal 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;
    }
}


Open the sessionEJb file and add the below code to the session bean and expose the method in local/remote interface and generate a data control for that.

Note:- Here in the below code "em" is a EntityManager.

public List<CustomBean> getCustomBeanFindAll() {
     String queryString =
            "select d.department_id, d.department_name, d.location_id, e.employee_id, e.first_name, e.last_name from departments d, employees e\n" +
            "where e.department_id = d.department_id";
     Query genericSearchQuery = em.createNativeQuery(queryString, "CustomQuery");
     List resultList = genericSearchQuery.getResultList();
     Iterator resultListIterator = resultList.iterator();
     List<CustomBean> customList = new ArrayList();
     while (resultListIterator.hasNext()) {
         Object col[] = (Object[])resultListIterator.next();
         CustomBean custom = new CustomBean();
         custom.setDepartmentId((BigDecimal)col[0]);
         custom.setDepartmentName((String)col[1]);
         custom.setLocationId((BigDecimal)col[2]);
         custom.setEmployeeId((BigDecimal)col[3]);
         custom.setFirstName((String)col[4]);
         custom.setLastName((String)col[5]);
         customList.add(custom);
     }
     return customList;
}

Open the DataControls.dcx file and create sparse xml for customBean. In sparse xml navigate to Named criteria tab -> Bind Variable section, create two binding variables deptId,fName.

BindVariables.png


In sparse xml navigate to Named criteria tab ->Named criteria, create a named criteria and map the query attributes to the bind variables.

CustomBeanCriteria.png


In the ViewController create a file jspx page, from data control palette drop customBeanFindAll->Named Criteria->CustomBeanCriteria->Query as ADF Query Panel with Table. Run the jspx page and enter values in search form with departmentId as 50 and firstName as "M". Named criteria will filter the query of a data source and display the result like below.

NamedCriteriaResult.png
About

Tips and Tricks from Oracle's JDeveloper & ADF QA

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today