Friday, March 16, 2012

Displaying the row count for an af:table

Problem

Say you have an af:table based on a view object.  You want to display the number of rows in the table.  Below I describe a couple ways to do it.  The solutions apply to ADF 11.1.1.3.

Solution 1

Create an af:outputText on the page.  Set the value to an EL expression that returns the estimated row count.  Use partialTriggers to refresh the row count when needed.

    <af:outputText value="  Row count: #{bindings.MyVOIterator.estimatedRowCount}"
                              id="ot4" partialTriggers="cb1 cb2"/>

Thanks to the unknown commenter for suggesting this solution.

Solution 2

1. Create a method in a managed bean to return the number of rows.  E.g.

    public int getLineCount() {
        BindingContainer bc = BindingContext.getCurrent().getCurrentBindingsEntry();
        DCBindingContainer bindings = (DCBindingContainer)bc;
        DCIteratorBinding iter = bindings.findIteratorBinding("MyVOIterator");
        return iter.getRowSetIterator().getRowCount();
    }


2.  Make sure there is a binding for the VO iterator ("MyVOIterator") in the page definition.

3. Create an af:outputText on the page.  Set the value to an EL expression that points to the managed bean method.  Use partialTriggers to refresh the row count when needed.

    <af:outputText value="Row count: #{backingBeanScope.MyBean.lineCount}"
                   id="ot2" partialTriggers="cb1 cb2"/>


Note on getRowCount() vs getEstimatedRowCount()

The ViewObjectImpl class has two methods that return the row count: getRowCount() and getEstimatedRowCount().  See this post for a good explanation of the differences.  Briefly:
  • getRowCount() is more accurate, but less performant.
  • getEstimatedRowCount() is potentially less accurate, but more performant. 

Thanks to Richard Ver Steeg for his insights on this topic.

    Monday, March 5, 2012

    Using the ADF BC userData object

    Introduction

    ADF BC provides a userData object that is stored in the session.  It is useful for storing data related to the user and/or the session.  However, there are some nuances you need to address.

    The following applies to ADF 11.1.1.3.

    The userData object

    The userData object is a Hashtable.  Key/value pairs are stored as type Object.  Here's an example of storing data.  Note that the "userId" in this example is originally of type oracle.jbo.domain.Number.

        protected void storeUserDetails(Number userId, String userName) {
            this.getSession().getUserData().put("userId", userId);
            this.getSession().getUserData().put("userName", userName);
        }


    You can then retrieve the values like this:

        Number userId =
            (Number)this.getSession().getUserData().get("userId");
        String userName = this.getSession().getUserData().get("userName");


    Problem

    However, when the Application Module passivates, the userData is lost.  To keep the userData, you must include custom code in your Application Module class.  This post has good example code for this.  Note however that the example code stores and retrieves the values as String in an XML "TextNode".  This causes a problem when you try to retrieve a Number after a passivation/activation cycle.  Executing this ...

        Number userId = this.getSession().getUserData().get("userId");

      ... will now cause "java.lang.ClassCastException: java.lang.String cannot be cast to oracle.jbo.domain.Number"

    Solution

    Use a helper method to retrieve Numbers from the userData object.

    Here' s the helper method:

        public Number getNumber(Object obj) {
            Number num = null;
            try {
                num = new Number(obj);    // Works when value is a String
            } catch (SQLException sqle) {
                num = (Number)obj;    // Works when value is an Object
            }
            return num;
        }


    Here's it's usage within a VO implementation class:

        MyAppModuleImpl am = (MyAppModuleImpl)this.getApplicationModule();
        Number userId =

            am.getNumber(am.getSession().getUserData().get("userId"));

    This helper method works whether the userData object has been passivated/activated or not.

    Tuesday, December 13, 2011

    An EO validation rule that involves a transient attribute

    Problem
    Say you have a VO with a transient attribute (e.g., AmountSpent), and an entity-based attribute (e.g., MaxAmount).  The value of AmountSpent (the transient attribute) is set when the application starts.  Then, the user enters a value for MaxAmount (the entity-based attribute) in the UI.

    Now, say you need to validate that MaxAmount is greater than AmountSpent.  The transient attribute does not exist in the EO, so a simple Compare validation rule is not possible.

    How can you do this? 

    Solution
    First, create a method on the Application Module to return the value of the transient attribute:

      public Number getAmountSpent() {
        AmountVOImpl vo = this.getAmountVO1();
        AmountVORowImpl row = (AmountVORowImpl)vo.getCurrentRow();
        return row.getAmountSpent();
      }

    Next, create a validation rule on the entity-based attribute (MaxAmount).  In this rule, compare the value of MaxAmount to a Groovy expression that calls the AM method. Here's the expression:

      source.getDBTransaction().getRootApplicationModule().getAmountSpent()

    Note that you need to use "source" at the start of the Groovy expression. The reason for this is explained in the Fusion Dev Guide.

    This rule will give you the validation you need.

    Monday, November 21, 2011

    Invoking a web service from an ADF managed bean


    In this post I describe how to invoke an external web service from an ADF managed bean.  The web service is represented by a web service proxy within the ADF Model project.  The proxy is exposed as a client method on the application module.  A method in a managed bean invokes the client method.

    This example uses JDeveloper 11.1.1.3.  The external web service is based on the Cue Cards example “Create a Web Service from a Java Class” found in the JDeveloper Help Center.

    See Chapter 13 “Integrating Web Services Into a Fusion Web Application” in the Fusion Developer’s Guide for more information.

    Create the proxy


    1.       In the Model project, launch the Create Web Service Proxy wizard.

    2.       Choose JAX-WS Style.



    3.       Enter the WSDL URL and select Copy WSDL Into project.


    4.       In the Specify Default Mapping Options window, enter package names that cleanly separate the proxy files from ADF BC components.  E.g., if your ADF BC components are under the package app1.model, then enter:
    ·         Package Name = app1.proxy.<proxy name>
    ·         Root Package for Generated Types = app1.proxy.<proxy name>.types


    NOTE:  If the web service has asynchronous callback methods, the “Generate As Async” checkbox is enabled.  If you do not plan to use the callback methods, uncheck this box box.  Leaving the box checked creates un-needed web services and a WAR file in the project.

    5.       If you do not use the asynchronous callback methods select “Don’t generate any asynchronous methods” in Step 6.


    6.       Click Finish.

    7.       The JDeveloper workspace looks like this.  Proxy files are cleanly separated from ADF BC components.  Note the jax-ws-catalog file under META-INF.  This is useful for editing the URL endpoint of the web service.



    Create an application module method to invoke the proxy


    1.       Generate an application module class and open it in the editor.  This is the <AppModule>Impl.java file.

    2.       Add a method that instantiates the proxy class and invokes the web service.  For example:

           public int getCreditRating(String ssn) {
               // Instantiate the proxy
               CreditRatingService svc = new CreditRatingService();
               CreditRating creditRating = svc.getCreditRatingSoap12HttpPort();
          
               // Invoke service
               int result;
               try {
                   result = creditRating.processRating(ssn);
                   System.out.println("  ==Got credit rating");
               } catch (Exception e) {
                   result = -1;
                   System.out.println("  ==Exception: " + e.getMessage());
                   e.printStackTrace();
               }
          
               // Return result
               return result;
           }


    3.       Add the new method to the application module client interface


    4.       Refresh the Data Control panel

    5.       Open the page you want to invoke the method from > go to the Bindings tab > add the operation as an Action Binding


    6.       You can now invoke the method from a managed bean, using code like this:

           public void onGetCreditRatingAction(ActionEvent actionEvent) {
               BindingContainer bc =
                   BindingContext.getCurrent().getCurrentBindingsEntry();
               OperationBinding ob = bc.getOperationBinding("getCreditRating");
               ob.getParamsMap().put("ssn", "123456789");
               System.out.println("  ==About to get credit rating ...");
               String result = (String)ob.execute();
               System.out.println("  ==Credit rating: " + result);
           }


    Saturday, November 19, 2011

    Learn ADF

    I often come across someone asking for advice on how to start learning ADF.  I point them to this blog post by Shay Shmeltzer.

    Another way for beginners to get their feet wet is to go thru the "Cue Cards" exercises under the JDeveloper Help Center.

    More advanced learners can go straight to the ADF Insider page.  This page has more than 40 recorded presentations on topics from basic to advanced.