Monday Nov 25, 2013

ADF Faces Layout Basics - New Whitepaper Released

As the title suggests, basics of layout components available in ADF Faces is covered in this whitepaper.

This paper can be accessed from this location : http://www.oracle.com/technetwork/developer-tools/adf/learnmore/adffaceslayoutbasics-2046652.pdf

In a nutshell, this paper talks about

  • different layout components available in ADF
  • their geometry management
  • example situations during which a particular component can be used
  • reverse engineering some of the popular websites and
  • solutions to few frequent layout scenarios

This paper will be very helpful for understanding different layout components offered by ADF Faces and also to use them effectively to build your website.

You can also browse through ADF Architecture Square Homepage to find this and other related whitepapers : http://bit.ly/adfarchsquare

Thursday Oct 17, 2013

Simple GET operation with JSON data in ADF Mobile

Usecase:

This sample uses a RESTful service which contains a GET method that fetches employee details for an employee with given employee ID along with other methods. The data is fetched in JSON format.

This RESTful service is then invoked via ADF Mobile and the JSON data thus obtained is parsed and rendered in mobile in a table.

Prerequisite:

Download JDev build JDEVADF_11.1.2.4.0_GENERIC_130421.1600.6436.1 or higher with mobile support. 

Steps:

Run EmployeeService.java in JSONService.zip. This is a simple service with a method, getEmpById(id) that takes employee ID as parameter and produces employee details in JSON format. 

Copy the target URL generated on running this service. The target URL will be as shown below:

http://127.0.0.1:7101/JSONService-Project1-context-root/jersey/project1

Now, let us invoke this service in our mobile application. For this, create an ADF Mobile application. 

Gallery

Name the application JSON_SearchByEmpID and finish the wizard.

appName

Now, let us create a connection to our service. To do this, we create a URL Connection. Invoke new gallery wizard on ApplicationController project. 

newGallery

Select URL Connection option.

urlConn

In the Create URL Connection window, enter connection name as ‘conn’. For URL endpoint, supply the URL you copied earlier on running the service. Remember to use your system IP instead of localhost. Test the connection and click OK.

conn

At this point, a connection to the REST service has been created.

Since JSON data is not supported directly in WSDC wizard, we need to invoke the operation through Java code using RestServiceAdapter. For this, in the ApplicationController project, create a Java class called ‘EmployeeDC’. We will be creating DC from this class.

javaclass

Add the following code to the newly created class to invoke the getEmpById method.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public Employee fetchEmpDetails(){

RestServiceAdapter restServiceAdapter = Model.createRestServiceAdapter();

restServiceAdapter.clearRequestProperties();

restServiceAdapter.setConnectionName("conn"); //URL connection created with this name

restServiceAdapter.setRequestType(RestServiceAdapter.REQUEST_TYPE_GET);

restServiceAdapter.addRequestProperty("Content-Type", "application/json");

restServiceAdapter.addRequestProperty("Accept", "application/json; charset=UTF-8");

restServiceAdapter.setRetryLimit(0);

restServiceAdapter.setRequestURI("/getById/"+inputEmpID);

String response = "";

JSONBeanSerializationHelper jsonHelper = new JSONBeanSerializationHelper();

try {

response = restServiceAdapter.send(""); //Invoke the GET operation

System.out.println("Response received!");

Employee responseObject = (Employee) jsonHelper.fromJSON(Employee.class, response);

return responseObject;

} catch (Exception e) {

}

return null;

}

Here, in lines 2 to 9, we create the RestServiceAdapter and set various properties required to invoke the web service. At line 4, we are pointing to the connection ‘conn’ created previously.

Since we want to invoke getEmpById method of the service, which is defined by the URL

http://IP:7101/REST_Sanity_JSON-Project1-context-root/resources/project1/getById/{id}

we are updating the request URI to point to this URI at line 9. inputEmpID is a variable that will hold the value input by the user for employee ID. This we will be creating in a while.

As the method we are invoking is a GET operation and consumes json data, these properties are being set in lines 5 through 7. Finally, we are sending the request in line 13.

In line 15, we use jsonHelper.fromJSON to convert received JSON data to a Java object. The required Java objects' structure is defined in class Employee.java whose structure is provided later. Since the response from our service is a simple response consisting of attributes like employee Id, name, design etc, we will just return this parsed response (line 16) and use it to create DC.

As mentioned previously, we would like the user to input the employee ID for which he/she wants to perform search. So, in the same class, define a variable inputEmpID which will hold the value input by the user. Generate accessors for this variable.

accessor

Lastly, we need to create Employee class. Employee class will define how we want to structure the JSON object received from the service. To design the Employee class, run the services’ method in the browser or via analyzer using path parameter as 1. This will give you the output JSON structure.

jsonOutput

Ours is a simple service that returns a JSONObject with a set of data. Hence, Employee class will just contain this set of data defined with the proper data types.

Create Employee.java in the same project as EmployeeDC.java and write the below code:

package application;

import oracle.adfmf.java.beans.PropertyChangeListener;

import oracle.adfmf.java.beans.PropertyChangeSupport;

public class Employee {

private String dept;

private String desig;

private int id;

private String name;

private int salary;

private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

public void setDept(String dept) {

        String oldDept = this.dept;

this.dept = dept;

propertyChangeSupport.firePropertyChange("dept", oldDept, dept);

}

public String getDept() {

return dept;

}

public void setDesig(String desig) {

String oldDesig = this.desig;

this.desig = desig;

propertyChangeSupport.firePropertyChange("desig", oldDesig, desig);

}

public String getDesig() {

return desig;

}

public void setId(int id) {

int oldId = this.id;

this.id = id;

propertyChangeSupport.firePropertyChange("id", oldId, id);

}

public int getId() {

return id;

}

public void setName(String name) {

String oldName = this.name;

this.name = name;

propertyChangeSupport.firePropertyChange("name", oldName, name);

}

public String getName() {

return name;

}

public void setSalary(int salary) {

int oldSalary = this.salary;

this.salary = salary;

propertyChangeSupport.firePropertyChange("salary", oldSalary, salary);

}

public int getSalary() {

return salary;

}

public void addPropertyChangeListener(PropertyChangeListener l) {

propertyChangeSupport.addPropertyChangeListener(l);

}

public void removePropertyChangeListener(PropertyChangeListener l) {

propertyChangeSupport.removePropertyChangeListener(l);

    }

}

Now, let us create a DC out of EmployeeDC.java.

createDC

 DC as shown below is created.

DC

Now, you can design the mobile page as usual and invoke the operation of the service. To design the page, go to ViewController project and locate adfmf-feature.xml. Create a new feature called ‘SearchFeature’ by clicking the plus icon.

feature

Go the content tab and add an amx page. Call it SearchPage.amx.

createPage

Call it SearchPage.amx. Remove primary and secondary buttons as we don’t need them and rename the header.

preview

Drag and drop inputEmpID from the DC palette onto Panel Page in the structure pane as input text with label.

inputText

Next, drop fetchEmpDetails method as an ADF button.

fetchButton

For a change, let us display the output in a table component instead of the usual form. However, you will notice that if you drag and drop Employee onto the structure pane, there is no option for ADF Mobile Table. Hence, we will need to create the table on our own.

To do this, let us first drop Employee as an ADF Read -Only form. This step is needed to get the required bindings. We will be deleting this form in a while.

form

finalPage

Now, from the Component palette, search for ‘Table Layout’. Drag and drop this below the command button. 

table tableStructure

Within the tablelayout, insert ‘Row Layout’ and ‘Cell Format’ components. Final table structure should be as shown below. Here, we have also defined some inline styling to render the UI in a nice manner.

<amx:tableLayout id="tl1" borderWidth="2" halign="center" inlineStyle="vertical-align:middle;"

width="100%" cellPadding="10">

<amx:rowLayout id="rl1" >

<amx:cellFormat id="cf1" width="30%">

<amx:outputText value="#{bindings.dept.hints.label}" id="ot7" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf2">

<amx:outputText value="#{bindings.dept.inputValue}" id="ot8" />

</amx:cellFormat>

</amx:rowLayout>

<amx:rowLayout id="rl2">

<amx:cellFormat id="cf3" width="30%">

<amx:outputText value="#{bindings.desig.hints.label}" id="ot9" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf4" >

<amx:outputText value="#{bindings.desig.inputValue}" id="ot10"/>

</amx:cellFormat>

</amx:rowLayout>

<amx:rowLayout id="rl3">

<amx:cellFormat id="cf5" width="30%">

<amx:outputText value="#{bindings.id.hints.label}" id="ot11" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf6" >

<amx:outputText value="#{bindings.id.inputValue}" id="ot12"/>

</amx:cellFormat>

</amx:rowLayout>

<amx:rowLayout id="rl4">

<amx:cellFormat id="cf7" width="30%">

<amx:outputText value="#{bindings.name.hints.label}" id="ot13" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf8">

<amx:outputText value="#{bindings.name.inputValue}" id="ot14"/>

</amx:cellFormat>

</amx:rowLayout>

<amx:rowLayout id="rl5">

<amx:cellFormat id="cf9" width="30%">

<amx:outputText value="#{bindings.salary.hints.label}" id="ot15" inlineStyle="color:rgb(0,148,231);"/>

</amx:cellFormat>

<amx:cellFormat id="cf10">

<amx:outputText value="#{bindings.salary.inputValue}" id="ot16"/>

</amx:cellFormat>

</amx:rowLayout>

    </amx:tableLayout>

The values used in the output text of the table come from the bindings obtained from the ADF Form created earlier. As we have used the bindings and don’t need the form anymore, let us delete the form. 

tableImg

One last thing before we deploy. When user changes employee ID, we want to clear the table contents. For this we associate a value change listener with the input text box.

valChange

Click New in the resulting dialog to create a managed bean.

valChangeDialog managedBean

Next, we create a method within the managed bean. For this, click on the New button associated with method. Call the method ‘empIDChange’.

newMethod empIDChange

Open myClass.java and write the below code in empIDChange().

public void empIDChange(ValueChangeEvent valueChangeEvent) {

// Add event code here...

//Resetting the values to blank values when employee id changes

AdfELContext adfELContext = AdfmfJavaUtilities.getAdfELContext();

ValueExpression ve = AdfmfJavaUtilities.getValueExpression("#{bindings.dept.inputValue}", String.class);

ve.setValue(adfELContext, "");

ve = AdfmfJavaUtilities.getValueExpression("#{bindings.desig.inputValue}", String.class);

ve.setValue(adfELContext, "");

ve = AdfmfJavaUtilities.getValueExpression("#{bindings.id.inputValue}", int.class);

ve.setValue(adfELContext, "");

ve = AdfmfJavaUtilities.getValueExpression("#{bindings.name.inputValue}", String.class);

ve.setValue(adfELContext, "");

ve = AdfmfJavaUtilities.getValueExpression("#{bindings.salary.inputValue}", int.class);

ve.setValue(adfELContext, "");

}

That’s it. Deploy the application to android emulator or device. Some snippets from the app.

Snapshot1 Snapshot2

Monday Oct 07, 2013

XML Parsing in ADF Mobile

Usecase:

In this usecase, we have a web service that returns employee details(like name, designation, salary etc.) for an employee with given ID. In the mobile app, we will be calculating tax on the returned salary and displaying it to the user in a pop-up.

To do this, we will be parsing the returned XML to fetch the current salary and add a tax of 5% on it.

Here, we are assuming that tax is 5% of the current salary.

Steps:

Run EmployeeService.java in RESTApp.zip. Copy the target URL. 

Create a new ADF Mobile application. 

Name the application ‘XmlParsingSample’ and finish the wizard.

Create a URL Connection in the ApplicationController project.

Name the connection ‘conn’ and suppy the target URL copied earlier. Remember to use IP instead of localhost. Test the connection and click OK to close the wizard.

Since the service returns XML data, also create a Data control. This will be useful for designing the UI page. To create DC, invoke URL Service data control wizard in the ApplicationController project.

Test the URL connection in the last step and finish the wizard.

Now, let us create the AMX pages. Open adfmf-feature.xml present in ViewController project. Add a new feature ‘EmpFeature’ by clicking the plus icon as shown.

Go to the content tab and add an AMX page.

Call it EmpPage.amx. We don’t need the primary and secondary actions. So, uncheck them.

Drag and drop ‘id’ parameter from the DC palette onto the Panel Page of the Structure pane for this page.

Drag and drop a button from the component palette onto the page. Change the text of the button to ‘Get salary’.

As we want to invoke the service on click of this button, let us associate an ActionListener with this. Focus on the button and from the PI, select the arrow button for Action Listener propery and click Edit to bring up the below dialog. Click New to create a new managed bean.

Name the manged bean as  myBean and class as myClass.

Next, create a new method called calculateTax.

Go to myClass.java. In calculateTax method, write the code provided in the below table. Use the imports:

import oracle.adfmf.amx.event.ActionEvent;

import oracle.adfmf.dc.ws.rest.RestServiceAdapter;

import oracle.adfmf.framework.api.AdfmfContainerUtilities;

import oracle.adfmf.framework.api.Model;

import org.xmlpull.v1.XmlPullParser;

import org.kxml2.io.KXmlParser;

import java.io.Reader;

import java.io.StringReader;

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.

31.

32.

33.

34.

35.

36.

37.

38.

39.

40.

41.

42.

43.

44.

45.

46.

47.

48.

49.

50.

public void calculateTax(ActionEvent actionEvent) {

// Add event code here...

RestServiceAdapter restServiceAdapter = Model.createRestServiceAdapter();

restServiceAdapter.clearRequestProperties();

restServiceAdapter.setConnectionName("conn");

restServiceAdapter.setRequestType(RestServiceAdapter.REQUEST_TYPE_GET);

restServiceAdapter.addRequestProperty("Content-Type", "application/xml");

restServiceAdapter.addRequestProperty("Accept", "application/xml; charset=UTF-8");

restServiceAdapter.setRetryLimit(0);

Object emppIDObj = AdfmfJavaUtilities.evaluateELExpression("#{bindings.id.inputValue}");

String empIDString = emppIDObj.toString();

restServiceAdapter.setRequestURI("/byID?id="+empIDString);

String response = "";

try {

response = restServiceAdapter.send("");

System.out.println("Response received!");

String name = "";

int sal = 0;

int salAfterTax = 0;

KXmlParser parser = new KXmlParser(); //create a parser instance

Reader stream = new StringReader(response);

parser.setInput(stream);

parser.nextTag();

parser.require(XmlPullParser.START_TAG, null, "employee");

while (parser.nextTag() != XmlPullParser.END_TAG) { //loop through until you encounter an end tag

parser.require(XmlPullParser.START_TAG, null, null); //go to start tag

String tagName = parser.getName(); //get the name of the current tag

String tagValue = parser.nextText(); //get the value within the current tag

if (tagName.equals("name")) { //if current tag is ‘name’, assign its value to variable ‘name’

name = tagValue;

}

if (tagName.equals("salary")) { //if current tag is ‘salary’, calculate salary after tax

System.out.println("Salary:" + tagValue);

sal = Integer.parseInt(tagValue);

salAfterTax = (int)(sal - sal * 0.05);

}

parser.require(XmlPullParser.END_TAG, null, tagName); //go to end tag of current tag

//parse through all the tags

}

parser.require(XmlPullParser.END_TAG, null, "employee");

parser.next();

parser.require(XmlPullParser.END_DOCUMENT, null, null);

AdfmfContainerUtilities.invokeContainerJavaScriptFunction("EmpFeature", "navigator.notification.alert",

new Object[] { "Salary for employee " + name +

" is " + sal +

". After tax deduction, salary is:" +

salAfterTax, "", "Note", "OK!" });

} catch (Exception e) {

System.out.println("Exception is:" + e);

}

}

}

 Lines 3 to 15 set various properties required to invoke the web service. In lines 10 and 11, we are getting the value input by the user and converting that to String inorder to pass the parameter to the service. In line 20, we are creating a kXml parser instance. kXML is one of the core ADF Mobile libraries that provides API that you can use to parse XML. As the parser needs a Reader object, we are converting our services’ response into Reader in line 21 and passing it in line 22.

 The response returned by our service is of the form

<?xml version="1.0" encoding="UTF-8"?>
<employee>
<dept>HR</dept>
<desig>Officer</desig>
<id>2</id>
<name>Myra</name>
<salary>70000</salary>
</employee>

To parse this response, we are setting the parser to the first tag ‘employee’ in line 24. In lines 25 through 42, we are going through the XML elements. The comments are in-line for these lines. Finally, lines 43 through 47 are for displaying the pop-up. To display the pop-up, we are utilizing AdfmfContainerUtilities.invokeContainerJavaScriptFunction. For more information, see link.

As mentioned in the article, we make use of the javascript function which comes from phonegap-1.0.0.js. Hence, we need to include an entry to this javascript file in our AMX page. Put the below entry in EmpPage.amx above the panelPage.

<script type = "text/javascript" charset="utf-8" src="../../../www/js/phonegap-1.0.0.js"></script>

That’s it. Deploy the app to a device or an emulator. Some snippets from the final app. 

Enjoy coding! 

Wednesday Jul 31, 2013

EJB DC - Using JPQL constructor expressions

In JPA 2.0, a new feature is provided - "Constructor Expressions in the SELECT Clause". This feature is mainly useful for queries with multiple Select expressions, where custom result objects is necessary. This feature works with Oracle JDeveloper 12.1.2.0.0

Implementation Steps

Create Fusion Web Application with entities based on Departments and Employees, then create a session bean.

Create a Java class "DeptAndEmp" and add the below code.

public class DeptAndEmp implements Serializable {
    private String departmentName;
    private String email;
    private String firstName;
    private String lastName;
    public DeptAndEmp() {
        super();
    }

    public DeptAndEmp(String departmentName, String email, 
		String firstName, String lastName) {
        this.setDepartmentName(departmentName);
        this.setEmail(email);
        this.setFirstName(firstName);
        this.setLastName(lastName);
    }

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

    public String getDepartmentName() {
        return departmentName;
    }

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

    public String getEmail() {
        return email;
    }

    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 session bean and paste the below method code and expose the method filterDeptEmpResult() method in local/remote interface.

public List<DeptAndEmp> filterDeptEmpResult() { String qlString = "SELECT NEW model.util.DeptAndEmp(dept.departmentName, emp.email, emp.firstName, emp.lastName) FROM Employees emp, Departments dept where emp.departments.departmentId = dept.departmentId"; TypedQuery<DeptAndEmp> query = em.createQuery(qlString, DeptAndEmp.class); return query.getResultList(); }

Note:- In the Query "model is a package name and TypedQuery is a JPA query that returns a specific type of Object. TypedQuery eliminates the need to cast the query result to a specific type or having to add a SuppressWarnings annotation to eliminate compiler warnings about unchecked conversions. TypedQuery is well explained in Java Persistence Architecture 2.0 - Using The New TypedQuery Interface

Create a Sample Java Client and add the below code in main method.

SessionEJB sessionEJB = (SessionEJB) context.lookup("JPAConstructorApp-Model-SessionEJB#model.SessionEJB");

for (DeptAndEmp deptAndEmp : (List<DeptAndEmp>) sessionEJB.filterDeptEmpResult()) { System.out.println("departmentName = " + deptAndEmp.getDepartmentName()); System.out.println("email = " + deptAndEmp.getEmail()); System.out.println("firstName = " + deptAndEmp.getFirstName()); System.out.println("lastName = " + deptAndEmp.getLastName()); System.out.println("======================================="); }

Deploy the sessionFacade and run the java client. In the console employees details will be displayed as below.

Add-Edit multiple rows using EJB DC

Let us take a scenario where in users wants to add/edit multiple records in the ADF table,  earlier with stateless session bean we need have work around to achieve this scenario.

This scenario can be achieved using stateful session bean, the application-managed transaction model for EJB/POJO data controls which additionally maintains a cache of managed entities and using commit operations user will be able to add/edit multiple records.

Implementation Steps

Create Java EE Web Application with entity based on Dept(sequences enabled on deptId), then create a stateful session bean with Transaction Type as "CMT with Explicit Commit" and data control for the session bean.  "CMT with Explicit Commit" supported in Oracle JDeveloper 12.1.2.0.0

In View controller project, create jspx page. Drop deptFindAll->Table/List View as ADF Table with  multi-selection option enabled.



Run the jspx page (i.e the web page look something like the image shown below). Notice here the commit button will be disabled. Once user click on the create button, commit button will get enabled.



Here we will create two records, so clicking the create button twice. Enter the dept details and click on commit button to save the records. You can also configure @SequenceGenerator/@TableGenerator to auto generate the DeptNo.


Result page should contains newly added records.

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



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

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

Friday Mar 02, 2012

Refreshing One Column based on the value of Another Column in ADFdi Table

When using ADF Desktop Integration, quite frequently, we get into a situation where we would like to refresh one column based on the value of another column. In ADF Faces, we can achieve this by setting the autoSubmit property and partialTriggers property for the corresponding columns.

However, in ADFdi, we do not have such option. Though we can achieve this by using LOVs and Dependent LOVs. But, in some scenarios we would like to achieve this when using an Input Text Component.

In this article, we will simulate this Auto Refresh functionality in a ADFdi Table.

Note : Since we would be using VBA code to achieve this, we can use this only on the Macro Enabled Excel Workbooks.

Let us assume that we have a View Object based on the Emp table. We could take an example of having a transient attribute in the VO, that gives the sum of Salary and Commission attributes.

SalPlusComm.jpg



In the above example, we've added a new transient attribute (SalPlusComm) to the EmpVO, that would give the sum of Sal and Comm attributes. Since we need this attribute to get refreshed when either Sal or Comm attribute changes, we set the Sal and Comm attributes as Dependencies. Also, we set the AutoSubmit property (under UI Hints tab) for the Sal and Comm attributes.

SalCommAutoSubmit.jpg


Now, we are done with the model layer. We can now, create a jspx page and then Drag and Drop EmpView as ADF Table. After this, we create an Excel Workbook (macro enabled), enable ADF Desktop Integration for it, set the required Workbook Properties, and then add a Table based on the EmpView.

EmpTable.jpg



As there are no straight forward way in ADFdi to trigger a request to server when a value of a cell is changed, we will now add a DoubleClickActionSet for the Sal and Comm columns. This DoubleClickActionSet will have the Table.RowUpSync and Table.RowDownSync actions.

RowUpSyncDownSync.jpg


Above example image shows the DoubleClickActionSet for Sal column. In the same manner, we need to add the DoubleClickActionSet for the Comm column as well.

Now, we have the workbook, that would fetch the SalPlusComm attribute (after recalculation in the model), when we change the Sal / Comm attribute and then double click on that column. To do this automatically when the user tabs out / presses enter key on the cells, we'll write a bit of VBA Code on the Worksheet where we've this table (Go to Developer Tab and Click on Visual Basic).

Private Sub Worksheet_Change(ByVal Target As Range)
  If Target.Column = 14 Or Target.Column = 15 Then
    Target.Select
    Application.CommandBars("Cell").Controls("Invoke Action...").Execute
  End If
End Sub
Excel would trigger Worksheet_Change event when a cell in the worksheet is modified. So, we would code our logic in that event. The above code assumes that Sal column is present in the N (14th) column on the worksheet and Comm column is present in O (15th) column. So, we would execute our logic only when the contents in these two columns change.

ADFdi would provide a context menu (Invoke Action...) when a DoubleClickActionSet is added to a particular column. We'll make use of that context menu and invoke it programatically.


InvokeAction.jpg



We invoke that context menu programatically using the following line of code

Application.CommandBars("Cell").Controls("Invoke Action...").Execute

Now, we run our workbook, modify the value of Sal column for any row and tab out of that field would automatically update the value of SalPlusComm column.


EmpTable1.jpg
EmpTable1.jpg


Here, a simple example (Transient Attribute) is taken for the explanation. In the similar fashion, we can also have a DoubleClickActionSet to contain a method in the Impl that would perform this calculation as well.

Tip: If you are not able to view the image fully, right click on the image and choose View Image option to see it completely.


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