Showing posts with label ADF. Show all posts
Showing posts with label ADF. Show all posts

Wednesday, February 17, 2016

Hide Newly Created/Uncommitted Rows in ADF table

For a scenario where you want to hide the newly created table rows which are not yet committed to the database:




Suppose we have an input form which lets us add a new employee and there is a table which displays all the employees. When we add a new employee using the input form a new row also gets added into the table. We may not want to display this new row until all the fields have been entered and the new employee data has been committed to database. To avoid displaying this new row in the table:

1. Create a transient attribute in the entity object and use it to identify whether the row is new by checking its entity state


    public String getNewRow() {
        if(this.getEntityState() == Entity.STATUS_NEW){
            return "Y";
        }
        else{
            return "N";
        }
    }


2. In the table bindings check Enable Filter and use the transient attribute as a filter. Use the filter value which corresponds to non new rows



Now only the rows which are fetched from the database and are not new will be displayed on the page



<< A Request >>
  Please contribute in making this world a better place to live in by adopting a pet from your nearest pet shelter (btw cats are my favourite :-) )
<< Thank You >>

Wednesday, February 4, 2015

Creating ADF Tree Table Programatically, ArrayList cannot be cast to JUCtrlHierBinding

ADF tree table can easily be created using POJO as data source in the way described in these blogs Blog1, Blog2

But if you try to access data of that tree from a managed bean, you get java.util.ArrayList cannot be cast to oracle.jbo.uicli.binding.JUCtrlHierBinding exception. And this error is caused because the tree data is not being accessed through the ADF binding layer. To resolve this issue, instead of using a POJO class we need to use a POJO Data Control as the ADF model for tree data

Here is the updated way of creating tree table based on POJO data:

- Create an Employee class
public class Employee {
 private String name;
 private String location;
 private List<Employee> directs;

 public Employee(String name, String loc, List<Employee> directs){
    this.name = name;
    this.location = loc;
    this.directs =  directs;
  }
  public String getName() {
    return name;
  }
  public String getLocation() {
    return location;
  }
  public List<Employee> getDirects() {
    return directs;
  }
}

- Create an EmployeeBean Class
public class EmployeeBean {
    private List<Employee> allEmployees;
    public EmployeeBean() {
        super();
    }
    public void setAllEmployees(List<Employee> allEmployees) {
        this.allEmployees = allEmployees;
    }

    public List<Employee> getAllEmployees() {
        List<Employee> directList = new ArrayList<Employee>(); 
        directList.add(new Employee("Ken", "New York", new ArrayList<Employee>()));
        Employee manager1 = new Employee("John", "London", directList);
        directList= new ArrayList<Employee>();
        directList.add(new Employee("Ramesh", "London", new ArrayList<Employee>()));
        Employee manager2 = new Employee("Ravi", "Bangalore",directList);
        directList= new ArrayList<Employee>();
        directList.add(new Employee("Rakesh", "Pune", new ArrayList<Employee>()));
        Employee manager3 = new Employee("Raju", "Pune",  directList);

        allEmployees = new ArrayList<Employee>();
        allEmployees.add(manager1);
        allEmployees.add(manager2);
        allEmployees.add(manager3);
        return allEmployees;
    }
}


- Right click on EmployeeBean class and select 'Create Data Control'. This will create an EmployeeBean data control that can be used to create the tree table.
- Drag and drop allEmployees from EmployeeBean data control on to the page as a tree component.

Now, you can access tree data without any issues

Assigning default search values for LOV Search and Select popup

Values for search attributes on LOV Search and Select popup can be preset using a launchPopupListener method

<af:inputComboboxListOfValues id="inpClv2"
    popupTitle="Search and Select: #{bindings.StateProvince.hints.label}"
    value="#{bindings.StateProvince.inputValue}"
    label="#{bindings.StateProvince.hints.label}"
    model="#{bindings.StateProvince.listOfValuesModel}"
    required="#{bindings.StateProvince.hints.mandatory}"
    columns="#{bindings.StateProvince.hints.displayWidth}"
    shortDesc="#{bindings.StateProvince.hints.tooltip}"
    launchPopupListener="#{backingBeanScope.lovBean.stateLaunchPopupListener}"> 
</af:inputComboboxListOfValues>


Suppose if we want to preset CountryId in Search and Select popup for State LOV, the launchPopupListener code will look like:
public void stateLaunchPopupListener(LaunchPopupEvent launchPopupEvent)
{
    UIXInputPopup lovComponent = (UIXInputPopup)launchPopupEvent.getSource();
    ListOfValuesModel model = lovComponent.getModel();
    if (model != null)
    {          
        QueryDescriptor queryDesc = model.getQueryDescriptor();
        ConjunctionCriterion conCrit = queryDesc.getConjunctionCriterion();
        List<Criterion> criterionList = conCrit.getCriterionList();
        for (Criterion criterion: criterionList)
        {
            AttributeDescriptor attrDesc = ((AttributeCriterion) criterion).getAttribute();
            if (attrDesc.getName().equalsIgnoreCase("CountryId")) 
            {
                List values = ((AttributeCriterion) criterion).getValues();
                values.set(0, "US"); 
            }
        }
    }           

}

Sunday, January 11, 2015

Change bar data based on selected slice: ADF Pie Graph

Let us see how we can programmatically change Y1Axis bar data based on the selected slice on a pie graph.

First we need to set up the data for pie graph
 - Create Countries, Restaurants and Revenue tables and populate some data in those based on the script, which has been  taken from  AMIS blog
 - Create a viewObject named CountryFinanceVO with below query:
SELECT countries.id country_id, countries.name country_name, SUM(revenue) country_revenue, SUM(costs) country_costs
FROM dve_countries countries, dve_restaurants  restaurants, dve_revenue revenue
WHERE  countries.id   = restaurants.country_id
AND    restaurants.id = revenue.restaurant_id
GROUP BY countries.id, countries.name
  - Create an application module named GraphAM and add a CountryFinanceVO instance into it

Drag CountryFinanceVO on to a page as pie graph of Ring-Bar type.
In the next window use following configuration


Add a shapeAttributes component to pieGraph and specify a clickListener, which will listen to the click event on a pie slice
<dvt:pieGraph id="pieGraph1"
                      value="#{bindings.CountryFinancesVO1.graphModel}"
                      subType="RING_BAR" shortDesc="Pie graph"
                      binding="#{pageFlowScope.graphBean.pieGraphBinding}"
                      dataSelection="single">
    <dvt:shapeAttributesSet>
        <dvt:shapeAttributes component="GRAPH_SLICE" clickable="true"
                     clickListener="#{pageFlowScope.graphBean.pieClickListener}"/>
    </dvt:shapeAttributesSet>
    <dvt:background>
        <dvt:specialEffects/>
    </dvt:background>
    <dvt:graphPieFrame/> 
    <dvt:y1Axis/>
    <dvt:sliceLabel/>
    <dvt:pieLabel rendered="true"/>
    <dvt:legendArea automaticPlacement="AP_NEVER"/>
</dvt:pieGraph>



Pie graph will always show Y1Axis bar corresponding to the data of first row in the view object rowset. So, we will need to make the row corresponding to the slice clicked as the first row of the rowset in order to show the related data on the Y1Axis bar. To achieve this, we will modify our View Object query as below

SELECT countries.id   country_id,      countries.name country_name,      SUM(revenue)   country_revenue,      SUM(costs)     country_costs
FROM   dve_countries    countries,      dve_restaurants  restaurants,     dve_revenue      revenue
WHERE  countries.id   = restaurants.country_id
AND    restaurants.id = revenue.restaurant_id AND countries.id = :querySelectedId
GROUP BY countries.id       , countries.name
union all
SELECT countries.id   country_id,      countries.name country_name,      SUM(revenue)   country_revenue,      SUM(costs)     country_costs
FROM   dve_countries    countries,      dve_restaurants  restaurants,     dve_revenue      revenue
WHERE  countries.id   = restaurants.country_id
AND    restaurants.id = revenue.restaurant_id AND countries.id != :querySelectedId
GROUP BY countries.id       , countries.name


We will use a bind variable which will contain the value of countryId for the selected slice.  We have the union of two queries, first gets the row with the selected country_id and second gets the all other rows. The bind variable has been defaulted with the value of first country_id.

In pieClickListener we will retrieve country_id of the selected slice and then invoke ExecuteWithParams with that as parameter.

  public void pieClickListener(ClickEvent clickEvent)
  {
    ComponentHandle handle = clickEvent.getComponentHandle();
    SeriesComponentHandle handleSeries = (SeriesComponentHandle)handle;

    if (handle instanceof SeriesComponentHandle)
    {
      Attributes[] seriesInfo =
        ((SeriesComponentHandle) handle).getSeriesAttributes();    

      String idValue = null;
      if (seriesInfo != null)
      {
        for (Attributes attrs: seriesInfo)
        {       
          idValue=
              (String) attrs.getValue(Attributes.ID_VALUE);
        }

        OperationBinding op =  ADFUtils.findOperation("ExecuteWithParams");
        op.getParamsMap().put("querySelectedId",idValue);
        op.execute();

        AdfFacesContext.getCurrentInstance().addPartialTarget(pieGraphBinding);

      }

    }

  }

 
  Now when you click on any slice, the slice will move to extreme right which is the location of the first series in the graph, and Y1Axis bar will display corresponding data.

Sql for pie graph

Script taken from AMIS-blog
It is used in my another post Change bar data based on selected slice: ADF Pie Graph  
CREATE TABLE DVE_COUNTRIES 
(
  ID NUMBER NOT NULL 
, NAME VARCHAR2(20) NOT NULL 
, CONSTRAINT DVE_COUNTRIES_PK PRIMARY KEY 
  (
    ID 
  )
  ENABLE 
);

ALTER TABLE DVE_COUNTRIES
ADD CONSTRAINT DVE_COUNTRIES_UK1 UNIQUE 
(
  NAME 
)
ENABLE;

INSERT INTO "DVT_USER"."DVE_COUNTRIES" (ID, NAME) VALUES ('1', 'Netherlands');
INSERT INTO "DVT_USER"."DVE_COUNTRIES" (ID, NAME) VALUES ('2', 'Sweden');
INSERT INTO "DVT_USER"."DVE_COUNTRIES" (ID, NAME) VALUES ('3', 'England');
INSERT INTO "DVT_USER"."DVE_COUNTRIES" (ID, NAME) VALUES ('4', 'France');
INSERT INTO "DVT_USER"."DVE_COUNTRIES" (ID, NAME) VALUES ('5', 'Australia');
INSERT INTO "DVT_USER"."DVE_COUNTRIES" (ID, NAME) VALUES ('6', 'China');
INSERT INTO "DVT_USER"."DVE_COUNTRIES" (ID, NAME) VALUES ('7', 'Italy');

CREATE TABLE DVE_RESTAURANTS 
(
  ID NUMBER NOT NULL 
, NAME VARCHAR2(30 BYTE) NOT NULL 
, COUNTRY_ID NUMBER NOT NULL 
, CONSTRAINT DVE_RESTAURANTS_PK PRIMARY KEY 
  (
    ID 
  )
  ENABLE 
);

ALTER TABLE DVE_RESTAURANTS
ADD CONSTRAINT DVE_RESTAURANTS_UK1 UNIQUE 
(
  NAME 
)
ENABLE;

ALTER TABLE DVE_RESTAURANTS
ADD CONSTRAINT DVE_RESTAURANTS_COUNTRIES_FK1 FOREIGN KEY
(
  COUNTRY_ID 
)
REFERENCES DVE_COUNTRIES
(
  ID 
)
ENABLE;

INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('1', 'The Frying Dutchman', '1');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('2', 'The Cheesecake Factory', '1');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('3', 'The Frosty Palace', '2');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('4', 'Dorsia', '3');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('5', 'Tom’s Restaurant', '3');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('6', 'Jackrabbit Slim’s', '3');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('7', 'Gusteau’s', '4');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('8', 'Texas Chili Parlor', '5');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('9', 'Big Kahuna Burger', '5');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('10', 'Bronto Burger', '5');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('11', 'Teriyaki Donut', '6');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('12', 'Nuovo Vesuvio', '7');
INSERT INTO "DVT_USER"."DVE_RESTAURANTS" (ID, NAME, COUNTRY_ID) VALUES ('13', 'Chotchkies', '7');

CREATE TABLE DVE_REVENUE 
(
  ID NUMBER NOT NULL 
, RESTAURANT_ID NUMBER NOT NULL 
, REVENUE NUMBER(10, 0) DEFAULT 0 NOT NULL 
, COSTS NUMBER(10, 0) DEFAULT 0 NOT NULL
, CONSTRAINT DVE_REVENUE_PK PRIMARY KEY (ID) ENABLE 
);

ALTER TABLE DVE_REVENUE
ADD CONSTRAINT DVE_REVENUE_RESTAURANTS_FK1 FOREIGN KEY
(
  RESTAURANT_ID 
)
REFERENCES DVE_RESTAURANTS
(
  ID 
)
ENABLE;

Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (1,1,30000,20000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (2,2,20000,10000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (3,3,30000,10000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (4,4,15000,10000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (5,5,25000,20000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (6,6,30000,0);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (7,7,10000,0);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (8,8,20000,10000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (9,9,30000,5000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (10,10,15000,15000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (11,11,20000,5000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (12,12,30000,10000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (13,13,5000,0);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (14,1,20000,10000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (15,2,10000,0);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (16,3,50000,30000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (17,4,10000,5000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (18,5,30000,25000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (19,6,40000,30000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (20,7,0,0);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (21,8,10000,1000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (22,9,50000,30000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (23,10,15000,10000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (24,11,25000,20000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (25,12,80000,30000);
Insert into DVT_USER.DVE_REVENUE (ID,RESTAURANT_ID,REVENUE,COSTS) values (26,13,5000,1000);

commit; 

Thursday, December 25, 2014

Implementing Drag and Drop functionality in ADF Calendar

To achieve Drag and Drop functionality for activities within the adf Calendar:

- Add 'Calendar Drop Target' as a child to Calendar:

<af:calendarDropTarget dropListener="#{pageFlowScope.calendarBean.calendarDropListener}" actions="MOVE">
    <af:dataFlavor flavorClass="org.apache.myfaces.trinidad.model.RowKeySet" discriminant="DnDAcivityModel"/>
</af:calendarDropTarget>

- Add the code below in calendarDropListener

  public DnDAction calendarDropListener(DropEvent dropEvent)
  {
    TimeZone timeZone1 = TimeZone.getDefault();
    Transferable transferable = dropEvent.getTransferable();
    CalendarActivity activity =
      (CalendarActivity) transferable.getData(DataFlavor.getDataFlavor(CalendarActivity.class));

    CalendarDropSite dropSite = (CalendarDropSite) dropEvent.getDropSite();
    Date dropTargetDate = dropSite.getDate(); //Date at drop target, time is defaulted at 00:00:00 in month view.
    System.out.println("Drop Date:"+ dropTargetDate);       
    
    Date sourceStartDate = activity.getStartDate(timeZone1); // Start date of dragged activity
    Date sourceEndDate = activity.getEndDate(timeZone1); // End date of dragged activity
    System.out.println("Old start date:" + sourceStartDate);
    System.out.println("Old end date:" + sourceEndDate); 

    // Calculate new start date for dragged activity
    Date targetStartDate = null;   
    // In day and week view
    if (calendarBinding.getView().equals("day") || calendarBinding.getView().equals("week"))
    {
      // Start date will be same as target date
      targetStartDate = dropTargetDate;
    }
    else // In month view
    {
        // New start date will be target date with start date time component
        // If  start date is 12/23/2014 07:38:16 and target date is 12/24/2014 00:00:00
        // then the new start date will be 12/24/2014 07:38:16
        targetStartDate = setStartDate(sourceStartDate, dropTargetDate);
    }
    
    // Calculate new end date by adding the difference of start and end date to new start date.
    Date targetEndDate =
      setEndDate(sourceStartDate, sourceEndDate, targetStartDate); 

    System.out.println("New start Date:" + targetStartDate);
    System.out.println("New end Date:" + targetEndDate);
  
    //Update Calendar Model with new start and end dates for dragged activity
    activity.setStartDate(targetStartDate, timeZone1);
    activity.setEndDate(targetEndDate, timeZone1);
  
    OperationBinding op =  ADFUtils.findOperation("Commit");
    op.execute();

    return dropEvent.getProposedAction();
  }

  public Date setStartDate(Date sourceStartDate, Date targetDate)
  {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(sourceStartDate);
    int hour = calendar.get(Calendar.HOUR);
    int minute = calendar.get(Calendar.MINUTE);
    int second = calendar.get(Calendar.SECOND);
    calendar.setTime(targetDate);
    int year = calendar.get(Calendar.YEAR); 
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DATE);
    calendar.set(year, month, day, hour, minute, second);    
    Date newStartDate = calendar.getTime();
    return newStartDate;
  }

  public Date setEndDate(Date sourceStartDate, Date sourceEndDate,
                         Date targetStartDate)
  {
    long diff = sourceEndDate.getTime() - sourceStartDate.getTime();
    int diffDays = (int) ((diff) / (1000 * 60 * 60 * 24));
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(sourceEndDate);   
    int tHour = calendar.get(Calendar.HOUR);
    int tMinute = calendar.get(Calendar.MINUTE);
    int tSecond = calendar.get(Calendar.SECOND);
    calendar.setTime(sourceStartDate);
    int sHour = calendar.get(Calendar.HOUR);
    int sMinute = calendar.get(Calendar.MINUTE);
    int sSecond = calendar.get(Calendar.SECOND);
    calendar.setTime(targetStartDate);
    calendar.add(Calendar.DATE, diffDays);
    calendar.add(Calendar.HOUR, tHour - sHour);
    calendar.add(Calendar.MINUTE, tMinute - sMinute);
    calendar.add(Calendar.SECOND, tSecond - sSecond);
    Date newEndDate = calendar.getTime();  
    return newEndDate;
  } 


For more details on adding Drag and Drop functionality to adf Calendar refer to Oracle docs

Wednesday, December 24, 2014

Finding User Identity using Identity Governance Framework

The Identity Governance Framework (IGF) enables secure exchange of identity-related information between users and applications and service providers (Oracle docs). Below is the code snippet for finding users based on their attribute values:

import oracle.security.jps.JpsContext;
import oracle.security.jps.JpsContextFactory;
import oracle.security.jps.service.idstore.IdentityStoreService;

import oracle.igf.ids.IdentityDirectory;
import oracle.igf.ids.ResultSet;
import oracle.igf.ids.SearchFilter;
import oracle.igf.ids.SearchOptions;
import oracle.igf.ids.User;
import oracle.igf.ids.UserManager;

public class IGFExample
{
  private void findUsersFromAttributeVal(String attrVal)
  {
    IdentityDirectory ids = null;
    UserManager uMgr = null;
    try
    {
      JpsContext context =
        JpsContextFactory.getContextFactory().getContext();
      IdentityStoreService idstore =
        (IdentityStoreService) context.getServiceInstance(IdentityStoreService.class);
      ids = idstore.getIdentityStore();
      uMgr = ids.getUserManager();

      //We are trying to find users whose description or displayname contains attrValue
      SearchFilter filter =
        new SearchFilter(SearchFilter.LogicalOp.OR, new SearchFilter("description",
                                                                     SearchFilter.Operator.CONTAINS,
                                                                     attrVal),
                         new SearchFilter("displayname",
                                          SearchFilter.Operator.CONTAINS,
                                          attrVal));
      SearchOptions searchOpts = new SearchOptions();
      //Sorting the results by firstname
      searchOpts.setSortAttrs(new String[]
          { "firstname" });
      ResultSet sr = uMgr.searchUsers(filter, searchOpts);
      while (sr.hasMore())
      {
        User user = sr.getNext();
        System.out.println("User Name: " + user.getSubjectName());
        System.out.println("User ID: " + user.getId());
      }
    }
    catch (Exception exp)
    {
      exp.printStackTrace();
    }
  }
}



Sunday, November 30, 2014

Update VO data programatically

Suppose there is a requirement to search for employees based on certain criteria and then add a bonus to their salaries. Let's say the UI for this includes an employee search, an input text box for entering the bonus amount and a button that should add the bonus to the salaries and update the VO.




Here is one way to achieve this:
- Create a method in AMImpl which updates salary for all the rows in currrent VO row set, and expose this method in AM client interface
public void updateSalary(double bonusPct)
{
 RowSetIterator empRow = getEmployeeView1().createRowSetIterator(null);
 EmployeeViewRowImpl currentRow = new EmployeeViewRowImpl();
 double currentSalary = 0;
 while (empRow.hasNext())
 {
  currentRow = (EmployeeViewRowImpl) empRow.next();
  currentSalary = currentRow.getSalary().doubleValue();
  currentRow.setSalary(new oracle.jbo.domain.Number(currentSalary + (currentSalary * bonusPct/100)));        
 }
 getDBTransaction().commit();
}
- Add the AM method in page bindings
- On button action listener, pass the bonus value from input box as parameter to AM method and execute it
public void addBonus(ActionEvent actionEvent)
{
 BindingContainer bc =
   BindingContext.getCurrent().getCurrentBindingsEntry();
 OperationBinding op = bc.getOperationBinding("updateSalary");
 op.getParamsMap().put("bonusPct", getBonus()); //getBonus() gets the entered bonus value
 op.execute();
}
Now, when you click Add Bonus button, updateSalary will be called and the bonus will be added to the salary of employess.



<< A Request >>
  Please contribute in making this world a better place to live by adopting a pet from your nearest pet shelter (btw cats are my favourite :-) )
<< Thank You >>

Monday, November 17, 2014

Show status indicator for long running method calls

ADF status indicator provides a way to notify users of a long running process on the page, but does not serve the purpose if the long running process starts before the page gets loaded. For example, there is a task flow that has a method call as its default activity. This method calls a SOAP service and hence takes a good amount of time to complete its execution. The next activity on the task flow is a web page, which only gets loaded when the method call is completed. So, if we add a status indicator on the page, it will not consider the execution time before the loading of page and also there is no way to directly use status indicator on top of a method call.


To notify users of a long running process in such scenarios, replace the method call activity on task flow with a view activity. This view activity will represent a page, which will execute the method call on page load event and will use status indicator to notify the user.


<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
  <f:view>
    <af:document id="d1">
      <af:messages id="m1"/>
<!-- Calling method on page load -->
      <af:serverListener type="onloadEvent"
                         method="#{backingBeanScope.initBean.callMethod}"/>
      <af:clientListener type="load" method="triggerOnLoad"/>
      <af:resource type="javascript">
        function triggerOnLoad(event)
        {
          AdfCustomEvent.queue(event.getSource(), "onloadEvent", {},false);
          return true;
        }
      </af:resource>
      <af:form id="f1">
<!-- Displaying status indicator in the middle of the page -->
        <af:panelStretchLayout id="psl1" startWidth="33%" endWidth="33%"
                                   topHeight="33%" bottomHeight="33%">
          <f:facet name="bottom"/>
          <f:facet name="center">
            <af:panelGroupLayout id="pglsi1" layout="vertical"
                                 styleClass="AFStretchWidth" halign="center"
                                 inlineStyle="font-size:medium; font-family:Arial, Helvetica, sans-serif;">             
              <af:outputText value="Loading ......" id="ot1"/>
              <af:spacer width="10" height="10" id="s2"/>
              <af:statusIndicator id="si1"/>              
            </af:panelGroupLayout> 
          </f:facet>
          <f:facet name="start">
            <af:panelGroupLayout id="pgl2"/>
          </f:facet>
          <f:facet name="end">
            <af:panelGroupLayout id="pgl3"/>
          </f:facet>
          <f:facet name="top">
            <af:panelGroupLayout id="pgl4"/>
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
    </af:document>
  </f:view>
</jsp:root>

Now, you will see a status indicator during the wait time till the page loads completely



<< A Request >>
  Please contribute in making this world a better place to live in by adopting a pet from your nearest pet shelter (btw cats are my favourite :-) )
<< Thank You >>

Sunday, November 16, 2014

Using xml clob data as data source

Suppose there is table that contains data, such as submitted user application, in xml format and their is a requirement to extract some info from that xml and display it on a web page. For example, the table below contains user application data as xml in AppData column. When a row is selected, the basic information of the user gets extracted from the xml and displayed on the page.


Here are the steps to achieve this:
1. Create JAXB content model from the xsd of xml data content. As a result, Java classes will be generated for each element type of  xsd


2. Create a Java bean class, which will be used later to create a data control. In this class add a property whose data type is the xsd root element type and a method which will populate data for generated Jaxb classes.

import generated.ApplicationType;
import java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

public class UserAppDataDC
{
  // XSD top element type
  private ApplicationType userAppData;

  public UserAppDataDC()
  {
    super();
  }

  //Method to populate app data in generated Jaxb classes
  public void getUserAppData(String appData)
  {
    try
    {
      JAXBContext jc = JAXBContext.newInstance(ApplicationType.class);
      StreamSource xml = new StreamSource(new StringReader(appData));
      Unmarshaller unmarshaller = jc.createUnmarshaller();
      JAXBElement<ApplicationType> jaxE1 =
        unmarshaller.unmarshal(xml, ApplicationType.class);
      setUserAppData(jaxE1.getValue());
    }
    catch (PropertyException pe)
    {
      pe.printStackTrace();
    }
    catch (JAXBException jaxbe)
    {
      jaxbe.printStackTrace();
    }
  }

  public void setUserAppData(ApplicationType userAppData)
  {
    this.userAppData = userAppData;
  }
  public ApplicationType getUserAppData()
  {
    return userAppData;
  }
}

3. Right click on the Java bean class and select 'Create Data Control'. Now you can see a data control corresponding to this class in Data Controls panel
4. Start building UI by adding a table component on the page. For getting table data we can simply use a View Object. We do not necessarily need to display the app data colum but the tree attribute binding for it should be available on the page. So, for this example we are using two data controls, a bean data control, which contains data collections corresponding to Jaxb classes and another which contains a VO on the table.



5. Add a method binding for bean data control method.
6. Add a panel form layout with the required attributes from bean data control.
7. Create a custom selection listener for the table in a managed bean. Bind both table and form layout components to managed bean attributes.

public void tableRowSelectionListener(SelectionEvent selectionEvent)
{
// Execute default selection listener behavior
   resolveMethodExpression("#{bindings.UserAppVO.collectionModel.makeCurrent}",
                                         null, new Class[]
            { SelectionEvent.class }, new Object[]
            { selectionEvent });

// Find the method binnding for bean data control method
   OperationBinding op = (OperationBinding)findOperation("getUserAppData");
// Set method parameter with the application data value of the selected row 
 op.getParamsMap().put("appData", getSelectedRowAttribute(getAppDataTable(), "AppData"));
   op.execute();
// Refresh bean data control iterator
    DCIteratorBinding iter = findIterator("UserAppDataDCIterator");
          iter.executeQuery();
          iter.refresh(DCIteratorBinding.RANGESIZE_UNLIMITED);
// Refresh form layout, which displays data extracted from xml
   AdfFacesContext.getCurrentInstance().addPartialTarget(getFormLayout());
    
}

When a row is selected on the table, the selection listener gets the app data from table attribute binding and pass it to the method, which populates data in Jaxb classes. Iterator binding is refreshed subsequently to get the updated data from bean data control.