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

Wednesday, 15 October 2008

Oracle ADF: Verifying Master-Detail relationships and throwing exception on "beforeCommit()" events

I have found no other way to validate master detail hierarchies, than using theEntityImpl.beforeCommit(TransactionEvent transactionEvent). Writing a validateEntity() function can only prove useful when examining the fields of the current record. There are cases however where you need to verify that the entire tree is correct. Consider for instance a cases where you need to verify that all items included in an order are eligible for seeding to the user's dispatch address. For cases like this you need to examine your data just before committing and the beforeCommit() does just that.

During before commit you can practically traverse your entire M/D tree using association accessors and verify that everything meets you business rules.

If anything fails to validate then raising a JboException will prevent the actual commit and the exception error message will appear on your tag in your jspx page. There is however only one small catch:

Unless you modify your application module configuration and set the jbo.txn.handleafterpostexc to true, all exceptions thrown inside the afterCommit() method result in a roll-back which completely destroys the data hierarchy just build. As Steve Muench puts it,

When this flag is true, the framework will take an in-memory snapshot of the pending state of the application module, and then use this snapshot to reinstate that same pending state in case any of the beforeCommit() methods fail with an exception. If everything goes successfully with no errors, the in-memory snapshot is not used and will just get garbage collected.

To change the jbo.txn.handleafterpostexc parameter, right click on your application module inside the application navigator, then select Configurations... from the pop-up menu, click the Edit button from the resulting dialog box and you are there.

Friday, 18 July 2008

Oracle ADF/ JSF: Mind your flags!

I do not know why JSF UI elements have a Disabled property. I mean I would prefer it if they instead offered an Enabled one. ADF bindings on the other hand have enabled properties, so UI tags usually look this :

<af:commandButton actionListener="#{bindings.Create.execute}"
                     text="#{res['commands.create']}"
                     disabled="#{!bindings.Create.enabled}"
                     action="addEdit"/>

How imagine having to wire in the UserInfo bean, I was talking about the other day. Ideally you would like to have the above Create button enabled when your user is an administrator i.e. #{UserInfo.admin == true} and when the Create binding is enabled, meaning #{bindings.Create.enabled == true}.

Since we now have a Disabled property, this means that the value assigned should be the opposite of #{UserInfo.admin == true && bindings.Create.enabled == true}. The EL expression for this is #{!(UserInfo.admin == true && bindings.Create.enabled == true)}. Using De Morgan's low we end up with #{!UserInfo.admin || !bindings.Create.enabled}.

I understand that for most people this is more than trivial, but I have to admit that is took me a good while to figure it out. (Does age come at a price or what?) I want to learn by my own mistakes. Therefore I thought I 'd put it down in witting, so I won't run into this again.

Tuesday, 15 July 2008

Oracle ADF: Viewlink based attributes vs Full Blown Queries

At one time or an other all of us must have dealt with an entity based View object whose SQL statement looked more or less like this :

SELECT UserInfo.USER_ID,
      UserInfo.USER_NAME,
      UserInfo.FULL_NAME,
      UserInfo.USER_ROLE,
      UserInfo.DEPARTMENT_ID,
      UserInfo.ACTIVE,
      Department.NAME,
      Department.ID
FROM USERS UserInfo, DEPARTMENTS Department
WHERE UserInfo.DEPARTMENT_ID = Department.ID (+)

This kind of VO may appear easy to create and manage when your schema contains two or three entities but things may end up pretty complicated as the number of associated entities increases. Let me show you the SQL statement of a view object displaying my version of the Service requests view in full detail.

  SELECT
      ServiceRequest.ID,
      ServiceRequest.REQUEST_DATE,
      TRUNC(REQUEST_DATE) AS SEARCH_DATE,
      ServiceRequest.REQUESTER_FIRST_NAME,
      upper( REQUESTER_FIRST_NAME) AS SEARCH_FIRST_NAME,
      ServiceRequest.REQUESTER_LAST_NAME,
      UPPER( REQUESTER_LAST_NAME) AS SEARCH_LAST_NAME,
      ServiceRequest.REQUESTER_TELEPHONE,
      ServiceRequest.LOCATION_STREET,
      ServiceRequest.LOCATION_NUMBER,
      ServiceRequest.LOCATION_AREA,
      ServiceRequest.LOCATION_ZIP,
      ServiceRequest.LOCATION_CITY,
      ServiceRequest.DEPARTMENT_FOR,
      Department.NAME AS DEPARTMENT_NAME,
      ServiceRequest.ASSIGNED_DATE,
      ServiceRequest.ASSIGNED_TO,
      AssignedTechnician.FULL_NAME AS TECHNICIAN_NAME,
      ServiceRequest.RESOLUTION_COMMENTS,
      ServiceRequest.REQUEST_STATUS,
      Status.NAME AS STATUS_NAME,
      ServiceRequest.PROBLEM_DESCRIPTION,
      AssignedTechnician.USER_ID,
      ServiceRequest.CREATED_BY,
      ServiceRequest.CREATED_ON,
      ServiceRequest.MODIFIED_BY,
      ServiceRequest.MODIFIED_ON,
      ServiceRequest.RESOLVED_ON,
      Department.ID AS ID1,
      Status.STATUS_ID,
      ServiceRequest.TELEPHONE_BY,
      TelephonedOperator.FULL_NAME AS TELEPHONE_OPERATOR_NAME,
      ServiceRequest.TELEPHONE_DATE,
      TelephonedOperator.USER_ID AS USER_ID1
 FROM
   SERVICE_REQUESTS ServiceRequest,
   DEPARTMENTS Department,
   STATUSES Status,
   USERS AssignedTechnician,
   USERS TelephonedOperator
 WHERE 
  (ServiceRequest.DEPARTMENT_FOR = Department.ID (+)) AND
  (ServiceRequest.REQUEST_STATUS = Status.STATUS_ID) AND
  (ServiceRequest.ASSIGNED_TO = AssignedTechnician.USER_ID(+)) AND
  (ServiceRequest.TELEPHONE_BY = TelephonedOperator.USER_ID(+))

When dealing with pure entity based view objects -- that is without a custom where clause -- then JDeveloper makes some descent effort in keeping your where clause straight. If you decide to add bind variables however, then JDeveloper completely looses it and you end up having to manage everything by hand. Where clauses have to be tampered with if you also decide that you need outer left joins and you cannot get away without using the primary keys of all the participating entities and then having to learn how to hide them.

I am never too happy when dealing with such monsters but users are always hungry for more and more information. I guess that this why every self respecting SAP table has at least 70 or more columns :-) ....

Anyway the thing that triggered my search for alternatives was the fact that on many occasions the reference fields of multi entity view objects would not update their values correctly especially after edits, until you issued a final commit, thus giving users one more reason to grumble. So instead of using the SQL query mode, I ended up using view link based transient attributes.

The idea is very simple. Create single entity based view objects containing all the relevant information. In our example Users and Departments. Then create a view link between the two and expose accessors to the client. Enable Java code generation for the primary view row. Last create a transient attribute to the source View (Users) that exposes the department name through the view link provided departments row member.

Let's follow the steps in more details....

For starters we can create two view objects UsersFullList and DepartmentsList with SQL statements like the following :

SELECT UserInfo.USER_ID,
      UserInfo.USER_NAME,
      UserInfo.FULL_NAME,
      UserInfo.USER_ROLE,
      UserInfo.DEPARTMENT_ID,
      UserInfo.ACTIVE
FROM USERS UserInfo

... for the Users View and ...

SELECT Department.ID,
      Department.NAME,
      Department.IS_TECH
FROM DEPARTMENTS Department

... for the departments. If you do not intend to edit Departments through your application, you may very well create a read only view object. Next we shall create a view link named UserAssignedToDept . Creating the view link is trivial, so I will show only you the images where we define the connection ....

and the view link properties :

The important thing to remember here is that since the view link cardinality is set to 1 → ∞ the department accessor will be of type Row instead of RowIterator. So if we next edit the UsersFullList view and create a Java class for the view row, then the code for the DepartmentInfo attribute will look exactly like this :

   /**Gets the associated Row using master-detail link DepartmentInfo
    */
   public Row getDepartmentInfo()
   {
       return (Row)getAttributeInternal(DEPARTMENTINFO);
   }

Creating a department name attribute for the the new view is now done by defining a transient attribute to the UsersFullList view

and then providing a getter method that looks like this :

   public String getDepartmentName()
   {
       String deptName;
       try {
           Row deptRow = this.getDepartmentInfo();
           deptName = (String )deptRow.getAttribute("Name");
       } catch (Exception e) {
           deptName = "";
       }
       return deptName;
   }

From the controller's side the view object looks exactly like it would if based on SQL and you don't have to hide the participating entity key attributes.

So is it worth it you might ask. The answer is -- as usually -- yes and no depending on the size of your project the complexity of your view objects and whether or not you prefer tampering with SQL code than Java. As I explained in the beginning of this posting the main advantage of this method is that your reference attributes will always display correctly since they are composed of pointers to the actual data of the view cache instead of cached entries. On the other hand if you project contains two view objects each composed from two entities, going through all that will be like killing a fly with a machine gun.

Friday, 11 July 2008

Oracle ADF: My simple UserInfo bean class

Some time ago, I posted a link to the JDeveloper forum regarding how to use he isUserInRole() method of the ExternalContext class in order to determine whether or not the current application user is assigned a role.

Since I have lost the original post, I am giving to display sample code showing how to reference the function, how to declare the bean in faces-config.xml and also how to use it.

Let's suppose that you our application has two roles. User and admin. Definitions ofr these roles is included in jazn-data.xml.

package mycompany.myapplication.view.beans;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

public class UserInfo {
   
    private boolean admin;
    private boolean user;

    public UserInfo()
    {
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();
        
        user = ec.isUserInRole("user");
        admin = ec.isUserInRole("admin");
    }
    
    public boolean isUser()
    {
        return user;
    }
    
    public boolean isAdmin()
    {
        return admin;
    }
}

The next step is to define the bean in faces-config.xml

  <managed-bean>
    <managed-bean-name>UserInfo</managed-bean-name>
    <managed-bean-class>mycompany.myapplication.view.beans.UserInfo</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>

Finally enabling or disabling a menu item property based on whether the user is an admin, is done like this

             <af:commandMenuItem text="#{res['menutabs.AdminAction']}"
                                    action="adminAction"
                                    disabled="#{!UserInfo.admin}"/>

By the same token preventing a page from being display is done like this

          <af:panelPage title="#{res['pages.pricelist.BasicFees.title']}"
                        rendered="#{UserInfo.admin}">

The truth of the matter is that I ended up using all that because I could not succeed in making j2ee security work for my application. Despite Chris Muir's hints on the JDeveloper forum, there was no way I could deny access to certain parts of my application, so I ended up enforcing security using my simple UserInfo bean that I hooked into the disabled and rendered properties of menu and panelPage tags of the protected pages like I showed before.

That way if anyone runs the application by going from the standard path, then he won't be able to access the admin pages and if he calls the pages directly, then he will see nothing.

Friday, 30 May 2008

Oracle ADF: Enriching the actions of a ViewObject

Based on the technique described two posts back regarding creation of a custom ViewObject that offers ready to drag actions, I now give you another simple yet very handy function. The showAllRows() function when added to the AB_BaseViewObject methods, allows you to create a button in a search page that removes all view criteria and executes the current query, very useful for cases when uses enter lots of view criteria and then need to display the entire contents of the entire row with a single click.

    public void showAllRows()
    {
        clearViewCriterias();
        executeQuery();
    }

Remember that in order for the action to appear in the JDeveloper Data Control Palette like in the following picture, you must include the showAllRows() function to the view objects client interface.

Wednesday, 21 May 2008

Oracle ADF: Duplicating a ViewObject's current row

I wanted to add a Create Like functionality that would allow a new row to be inserted in a ViewObject then drive the user to an edit page where he or she would make ant changes required for valid insertion of the new row to the database and avoid having to re-enter everything from scratch.

In order to accomplish this I created the following method in the ViewObject's implementation class.

import oracle.jbo.AttributeDef;
import oracle.jbo.Row;
import oracle.jbo.ReadOnlyAttrException;
import oracle.jbo.TooManyObjectsException;

. . .

    /**
     * create and insert a row similar to the current
     */
    public void createLike()
    {
        // get the current row to dublicate
        Row currentRow = getCurrentRow();
        if (currentRow == null)
            return;
            
        // create new one
        Row newRow = createRow();
        
        // get the attribute values for the current attribute
        Object[] myValues = currentRow.getAttributeValues();
        
        for (int i = 0; i < currentRow.getAttributeCount(); i++)
            try {
                AttributeDef d = getAttributeDef(i);
                    
                if (d.getUpdateableFlag() != AttributeDef.READONLY)                
                    newRow.setAttribute( i, myValues[i]);                
            }    
            catch (ReadOnlyAttrException roe) {
                continue;
            }
            catch (Exception e) {
                // it is almost certain that the primary key will be
                // violated so the user must do something in the 
                // page following execution of the method.
                if (!(e instanceof TooManyObjectsException))
                    System.out.println(e.toString());
            }
            
        insertRow(newRow);        
    }

The code works fine and copies the values all columns from the current row into a new one except that of the last column that constitutes the primary key. That way the user will have to make the appropriate changes to the new record, so it can be inserted correctly.

There are a few points worth mentioning in this apprach

  • if your view object contains attributes from multiple entity objects then the the key attributes of the referenced entities that gets inserted by the framework appear to be updatable -- hence the catch (ReadOnlyAttrException roe) line. I have brought this to the JDeveloper forum but got no suggestions.
  • Using the above technique requires that you expose the createLike() method to the ViewObject's client interface. See the previous post for details.
  • The most probable use of this method will be to place a command button into an <af:tableSelectOne> facet in order to drive your users to an edit page with values already set. On most occasions the edit page will contain a commit button that will perform the final post. My suggestion is to use a backing bean to invoke the commit action like I discuss in a previous article named ADF/JSF Avoiding uncaught exceptions after commit that will prevent the controller layer to change back to the browse page in case of any errors.

Thursday, 15 May 2008

Oracle ADF: A view object with cancelInsert and cancelEdit operations

I have already discussed the problem of providing a cancel button in an ADF/JSF edit page. Here is an alternative that allows the usage of a base view object that provides both a cancel edit and a cancel insert method.

I have come to believe that this method is the most appealing so here is a brief outline of how to use it :

First create the following class in the queries package of you model application.

package myapp.model.queries;

import oracle.jbo.Row;
import oracle.jbo.server.ViewObjectImpl;

public class AB_BaseViewObject
    extends ViewObjectImpl {

    public AB_BaseViewObject()
    {
    }
    
    /**
     * Refresh the view obejct's current row from the dataset practically 
     * undoing any changes.
     */
    public void cancelEdits() 
    {
        Row r = getCurrentRow();

        if (r != null) 
            r.refresh( Row.REFRESH_WITH_DB_FORGET_CHANGES);
    }
    
    /**
     * Undo changes and get rid of new rows
     */
    public void cancelInsert()
    {
        clearCache();      
    }
}

Next create Java Classes that extend the AB_ViewObject for all the view objects that require editing in your project. Publish the cancelXXX() methods on the view interface like in following picture.

Finally create buttons on the edit or create pages in the ViewController project that call the methods.

Thursday, 20 March 2008

Oracle ADF Two action listeners in one action

Some time ago, I run into a situation when I wanted to add two action listeners inside a single command element but I realized that this was just not possible. When I asked about that in the JDeveloper forums somebody told me that this could only be accomplished in code, but at that time my knowledge was just not enough

A couple of days ago I run into the same problem. I had a view page that when accessed wanted to know two things:

  • First was the ${row.rowKeyStr} of the row to display and ...
  • Next a session scope boolean parameter called #{sessionScope.returnHome} that indicated whether to return the user to the application home page or to another (the search in that case) page.

To summarize, I ended up with the a button called View record in two different pages both having a navigation case (also called view) linking to the view record page. in the application JSF diagram.

On both cases the expression #{row.rowKeyStr} had to be recorded in the JSF session scope along with a value for the returnHome flag that had to be true on one occasion and false on the other..

Following is the code I used to perform this. First the base class :

package myapp.view.backing;

import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;

class backing_SRListBase {

    public backing_SRListBase()
    {
    }
    
    public String viewButtonBase_action( boolean returnHome)
    {
        // access the faces context
        FacesContext fc = FacesContext.getCurrentInstance();
        Application app = fc.getApplication();
        
        // on both case row.rowKeyStr points to the current row key
        ValueBinding vbActRowKey = app.createValueBinding("#{row.rowKeyStr}");
        String rowKey = (String )vbActRowKey.getValue(fc);
                
        // in that case we must save that in sessionScope
        ValueBinding vbSessionScopeRowKey = app.createValueBinding("#{sessionScope.rowKey}");
        vbSessionScopeRowKey.setValue( fc, rowKey);
                
        // also save the return home boolean value
        ValueBinding vbReturnHome = app.createValueBinding("#{sessionScope.returnHome}");
        vbReturnHome.setValue( fc, new Boolean( returnHome));
        
        return "view";
    }
}

Finally, the backing bean for each of the two pages looks like this

package myapp.view.backing;

public class backing_OpenSRList 
    extends backing_SRListBase {

    public backing_OpenSRList()
    {
    }

    public String viewButton_action()
    {
        return viewButtonBase_action( false);
    }
}

Friday, 29 February 2008

Deploying Oracle ADF: What did I learn about caching

Our company's production application server is located at a site different than the one we do our development. Whenever we deployed a new application users were always complaining about long response times, but we usually blamed that on the network. When we moved away from jsps and started working with JSF pages things really got out of hand.

Response times took as much as 10 seconds when application pages where accessed via WAN, rising the user complaint level to maximum levels.

Additionally the network link appeared to be utilized below 80% and then application server response times as shown at OC4J administration website where much below 0.045 sec.

First thing we tried was to use Oracle's ADF Cache library as explained in Chapter 23 of the ADF Programmers guide for Forms/4GL Developers, without any success. To be precise the library was indeed incorporated into the application but the server response time was still as long as 9 or 10 seconds.

It was then when we asked for help from Oracle Support. So thanks to Kavintha we learned the following :

  • First thing we learned was that the problem was indeed related to caching. Every JSF page created using Oracle ADF utilizes a Javascript file named Common10_1_3_3_0.js with a file size of 120 KB. This is an external script referenced at every page. The problem was that this script was never cached so users had to download it every time causing the delays mentioned above. If this file -- and all related page images -- are cached, then the usual size of an ADF/JSF generated HTML code ranges between 1 and 2 KBytes.
    Enabling full caching options on the Oracle Apache Server can be done by uncommenting the # CacheNegotiatedDocs line of the httpd.conf file located in $ORACLE_HOME/Apache/Apache/conf.
  • Next thing was that if an application is protected by Single-Sign-On then in that case, no matter whether CacheNegotiatedDocs is commented out or not, Apache will always send no-cache headers practically undoing whatever the Apache configuration or the ADF Faces Cache library do.
    The way to allow caching for SSO protected applications is to modify mod_osso.conf located in $ORACLE_HOME/Apache/Apache/conf so the entry for each application looks like this :
            <Location /ProductionDelays>
                    OssoSendCacheHeaders off 
                    require valid-user
                    AuthType Basic
            </Location>
    
    More information about how to to disable the caching headers sent by SSO is available on Metalink note 226119.1. Also you may have a look at a one of my previous postings regarding using SSO which is available through here
  • Last thing we learned about this caching business was that when a JSF application is accessed via SSL, then again caching issues still apply. Firefox will disable caching by default whenever the user loads an https:// URL. IE on the other hand does cache SSL pages by default but this setting can change by a policy set by your system administrator. I realize that in this case Firefox uses the safest approach but unfortunately this will produce very poor results if ADF/JSF pages are to be accessed.
    Enabling disk caching of SSL pages in Firefox can be done by changing the value of the browser.cache.disk_cache_ssl parameter available from the about:config URL.
    IE on the other hand has an option called Do not save encrypted pages to disk available in the Security section of the Advanced internet properties tab. If you want IE to cache content from SSL pages this option must be unchecked.

Thursday, 6 December 2007

ADF BC Canceling edits in the current record of a ViewObject

The number of things that one man misses becomes frightening especially when something is as crucial as a cancel edits button in a record display form.

Up until now I have been using a Rollback action binding to cancel any edits. Of course that was also casing current record information to be lost, so I had to rely on Steve Muench's Restore Current Row After Rollback from his Not Yet Documented ADF Sample Applications in order to keep my data in sync..

Revelation came as I as was browsing for the 9999th time the 10.1.3.1 version of the Tutorial for Forms/4GL Developers. The magic function that makes a view record refresh and forget any changes is as simple as that.

public void cancelEditsToCurrentServiceRequest() {
    getServiceRequestMain().getCurrentRow().refresh( 
                            Row.REFRESH_WITH_DB_FORGET_CHANGES);
}

Shame on me!

On second thought Muench's approach is more centric and may come in handy especially if a program has many view objects through which users enter data. Of course writing cancelEdits functions is clearer but has to be done for every view object that we require to use. The choice is still ours.

Thursday, 29 November 2007

ADF-BC/JSF Performing case insensitive searches

I have been asked to implement a service request system that will allow a company to record service requests from customer via a telephone line. The difference with oracle's SRDemo application is that in my case SR's will be entered by telephone operators and be assigned to departments and not to technicians

Anyway the application still offers a SRSearch page that allows telephone operators and managers to filter the service request list and here is where my problem started since they asked me if the QBE system would match parts of the requester first or last name case insensitively.My first reaction was to tell them to enter everything in capital letters, but then I though I might give it a try and ended up like this

first I added two additional fields in my query named SearchFirstName and SearchLastName defined like this

  SELECT 
       .....
       ServiceRequest.REQUESTER_FIRST_NAME, 
       UPPER( REQUESTER_FIRST_NAME) AS SEARCH_FIRST_NAME, 
       ServiceRequest.REQUESTER_LAST_NAME, 
       UPPER( REQUESTER_LAST_NAME) AS SEARCH_LAST_NAME, 
       ......

then changed I used JavaScript in the SRSearch page to make sure that whatever the user enters in the SearchFierstName and SearchLastName fields is converted to upper case before posting.

The javascript file itself is as simple as this

/* ---------------------------------------------------------------------------
 * file SRSearchSubmit.js
 * contains code that allows to submit the SRSearch form with the first name
 * and last name fields converted to upercase.
 *
 * Date 03-12-2007
 * ---------------------------------------------------------------------------
 */
 function convert()
 {
    var firstName = document.forms["myADFForm"].elements["myADFForm:searchFirstNameField"].value;
    var lastName =  document.forms["myADFForm"].elements["myADFForm:searchLastNameField"].value;

    document.forms["myADFForm"].elements["myADFForm:searchFirstNameField"].value
      = firstName.toUpperCase();
    document.forms["myADFForm"].elements["myADFForm:searchLastNameField"].value
      = lastName.toUpperCase();

    return true;
 }

No the two final points. First we need to include the javascript file at the top of the page inside a <f:verbatim> tag. Next in order to get the names of the elements right, we need to set the id attribute of both the <h:form> and the <af:inputText> elements.

The page header part of the resulting JSF page looks like this

    <afh:html>
      <f:loadBundle basename="SRKomo" var="res"/>
      <afh:head title="#{res['application.title']}">        
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <link href="../css/oracle.css" rel="stylesheet" media="screen"/>
        <f:verbatim>
          <script src="../javascript/SRSearchSumbit.js" type="text/javascript"></script>
        </f:verbatim>
      </afh:head>
      <afh:body>
        <af:messages/>
        <h:form id="myADFForm" onsubmit="convert();">
        ...

And the definitions for the each of th two search fields like this

              <af:panelLabelAndMessage label="#{bindings.AllRequestsListSearchFirstName.label}">
                <af:inputText value="#{bindings.AllRequestsListSearchFirstName.inputValue}"
                              simple="true"
                              required="#{bindings.AllRequestsListSearchFirstName.mandatory}"
                              columns="#{bindings.AllRequestsListSearchFirstName.displayWidth}"
                              styleClass="searchTextField"
                              id="searchFirstNameField">
                  <af:validator binding="#{bindings.AllRequestsListSearchFirstName.validator}"/>
                </af:inputText>
              </af:panelLabelAndMessage>

and that did it.

Notes

  • The original info about how to place javascript into a JSF document comes from the Frank Nimphius' Blogbuster article ADF Faces: Submit a form with the Enter key.
  • I have found it very easy to understand the HTML produced by the Oracle JSF engine using Firebug. Especially the "Inspect element" function will locate the HTML code of each eleent you click on and that can save you an awfull lot of decoding trouble.

Friday, 9 November 2007

ADF/JSF Getting the current row with code executed from a table bucking bean

The following code comes from the JDeveloper Forums courtesy of Frank Nimphus. If added to any event listener fired from "inside" an ADF Table, it prints the row's 1'st attribute and the rowKey.

        FacesContext fctx = FacesContext.getCurrentInstance();
        ValueBinding vb = (ValueBinding) fctx.getApplication().createValueBinding("#{row}");
        JUCtrlValueBindingRef rwbinding = (JUCtrlValueBindingRef) vb.getValue(fctx);
        System.out.println("--- Key as String --"+rwbinding.getRow().getKey().toStringFormat(true));
        System.out.println("--- First Attribute ---"+rwbinding.getRow().getAttribute(1));

Thanks again Frank

ADF/JSF Avoiding uncought exeptions after commit.

When using Oracle JDeveloper 10.1.3.x, you can find it very tempting to create "Create/Edit Record" pages by dropping an ADF Form on a blank JSF page and then adding commit and rollback buttons in the place of the ok and cancel buttons with an action property that returns your users to the browse page.

So let's assume that your faces-config file contains a navigation case labeled "return", that leads from you edit page back to your browse page. In cases like these, the code for the commit button will more or less look like this

                      <af:commandButton 
                                     actionListener="#{bindings.Commit.execute}"
                                     text="Ok" 
                                     action="return"
                                     id="commitButton"
                                     disabled="false"/>

This approach, presents a very serious hazard, which is a bug in Oracle's JSF implementation. If any exception is thrown after commit, the framework moves to the page designated by the navigation case and the error messages are not being displayed at all.

To make matters worse, you end up with on open transaction and nothing else entered in this manner ever gets stored in your database while you users have no way to realize that something has gone wrong. I had raised an SR for this that ended up in a one off patch (5630740) for JDeveloper 10.1.3.2.

I run into this problem again in am application that I developed recently using JDeveloper 10.1.3.3. And then it hit me. The solution was as simple as the Egg of Columbus. The only thing that needs to be done is to bind the commit button action in a backing bean. JDeveloper will then change the button tag into something like :

                      <af:commandButton 
                                    text="Accept"
                                    id="commitButton"
                                    disabled="false"
                                    action="#{backing_Edit.commitButton_action}"/>

... create the following code :

    public String commitButton_action()
    {
        BindingContainer bindings = getBindings();
        OperationBinding operationBinding =
            bindings.getOperationBinding("Commit");
        Object result = operationBinding.execute();
        if (!operationBinding.getErrors().isEmpty()) {
            return null;
        }
        return "return";
    }

... and that does it. Commit exception messages are displayed correctly and everyone is happy.

In case you have many edit pages with different navigation case labels then you might wish to create a solution that is a little more sophisticated and closer to the OO paradigm. In a case like this I suggest that you create a base class to be the parent of all your page backing beans and add the following method.

public class MyBackingBase
{
    protected String executeCommit( BindingContainer bindings, String successValue)
    {
        OperationBinding commitBinding =
            bindings.getOperationBinding("Commit");
        
        commitBinding.execute();
                
        return commitBinding.getErrors().isEmpty() ? successValue : null;
    }
 }

Having done that the actual backing bean code could be something as simple as

    public String commitButton_action()
    {
        return executeCommit( getBindings(), "return");
    }

... and one more thing. The Copy as HTML function is not available again. I guess I 'll have to start looking why .. :-(

Wednesday, 17 October 2007

JSF How to create and use dependent list boxes

Back in 2006, Frank Nimphius has written two excellent articles about using AJAX style combo (or list) boxes in a web page that filter one another based on values of a master detail relationship. I have to admit that thanks to him, my users have one less reason to grumble :-)

The first one is entitled ADF Faces: How-to build dependent lists boxes with ADF and ADF Faces and the second one ADF Faces: How-to build dependent lists boxes with ADF and ADF Faces Part - II

Thank you Frank.

Thursday, 27 September 2007

ADF Tables. Changing the colour of negative valued columns

My users asked me if it would be possible to display negative values of an ADF table using a different colour than the rest.

The solution turned out to be something as simple as defining two CSS classes like this :

.redFont {
    color: Red;
}
.blueFont {
    color: Navy;
}

The last step was to bind the StyleClass property of the adf:outputText elements using the following EL expression :

#{row.myColumn.value > 0 ? 'blueFont' : 'redFont'}

Tuesday, 26 June 2007

Wrong date and time format when depolying an Application

All the credit for this post should go to John Stegeman for providing the solution. I will just copy the main points here, so I can find it more easily.

The story goes like this. People are seeing different date and time formats after deploying and running their applications on an Oracle IAS rather than their embedded OC4J where the dates and times display correctly.

The solution appears to be the addition of a <locale-config> tag inside the faces-config.xml file. The entire thread that explains everything can be accessed from here. Furthermore John also posted a link with all the two letter ISO country codes which is available from http://www.bcpl.net/~j1m5path/isocodes-table.html

One final remark. I have been deploying applications on Oracle IAS running on Linux (SuSE Linux 8 and SLES9), since version 9i and never run into anything like it. Could that be a Windows issue ? :-)

Monday, 18 June 2007

ADF Tables. Setting an alternative sort property

Suppose you have an ADF table somewhere inside a JSF page and you wish it to be sortable. This is fine as long as you define two things.

  • The sortproperty property of the adf:column tag which has to be one of the column names of the table binding and tell the system how to change the order by clause of the underlying view.
  • The sortbale which has to be set to true or false and determines whether to allow sorting by clicking on the column header or not

So this is a perfectly good column definition for an attribute called Matnr athat corresponds to the notorious SAP material number.

<af:column sortProperty="Matnr" sortable="true"
    headerText="#{bindings['PF_ForSale'].labels.Matnr}">
    <af:outputText value="#{row.Matnr}"/>
</af:column>

If you now happen to have a transient attribute and you wish to sort the table based on this attribute, then you just can't do it. The way I see it, sorting means changing the order by clause and re executing the entire query. ADF complains that there is simply no attribute by that name and throws an SQLException that comes straight from the JDBC driver.

My suggestion for a workaround that works most of the times is this : Since 90% pf transient attributes usually serve as simple lexical transformations of real database attributes, changing the sortProperty of the adf:column tag to the value of the real column might solve the problem

Let's suppose that the ViewObject of the previous example has a transient attribute called DisplayMatnr that simply removes the leading zeros of the SAP oriented material number stored in column Matnr. It goes without saying that if Matnr is sorted then so is DisplayMatnr. Therefore writing something like ...

<af:column sortProperty="Matnr" sortable="true"
    headerText="#{bindings['PF_ForSale'].labels.DisplayMatnr}">
    <af:outputText value="#{row.DisplayMatnr}"/>
</af:column>

... might bring the desired effect. There is one last warning though the attribute that you wish to sort by must be a part of the table definition in the bindings page

Tuesday, 15 May 2007

Oracle ADF Committing master and relevant detail rows simultaneously

There is no doubt that using a trigger to provide unique id's from a database sequence is the best way to handle primary keys. (By the way, I wonder which version of Oracle database will provide an auto-increment data type like MySQL?) There are cases however that require you to create master and detail records in the application server memory and then commit everything simultaneously like in the case of an e-shop where you have to keep both the order header and lines uncommitted until the user finally decides to place the order.

In cases like these, using the DBSequence type for the corresponding entity object attribute might get you into trouble, like I did just yesterday. Theory says that the value of a DBSequence key attribute is a (unique) negative number that later becomes the value assigned by the database trigger. What I did, was try to create detail rows programatically but the master attribute reference for these rows became the negative DBSequence value assigned to the header line at the start of the transaction. So when I tried to do the final commit I got a very clear error explaining that the foreign key constraint for the detail rows was violated.

The only reliable way I found to get myself out of this was to turn back to my old JDeveloper 10g Handbook (published back in 2004) and dig out the following code to allow my entities to obtain an id value directly from the database sequence, thus moving the trigger code from the DB to the application server. So overriding the create method for my custom order header entity object gave me something like this :


import oracle.jbo.server.SequenceImpl;

  ...

  /**
   * Provide a new order id from the SQ_ORDERS database sequence.
   */
  protected void create(AttributeList attributeList)
  {
      super.create(attributeList);
    
      // get a new sequence value for the order ID
      SequenceImpl sqOrders = new SequenceImpl("SQ_ORDERS", getDBTransaction());
      setOrderId( sqOrders.getSequenceNumber());
  }

Friday, 11 May 2007

Oracle ADF How to get the Date after n days and calculate the difference between two dates

I have used this little code fragment many times and each time I try to remember where -- and in which file -- was the last.

I guess that if I put it here, I will limit my search space significantly :-)

import oracle.jbo.domain.Date;
import oracle.jbo.domain.Number;
import java.sql.Timestamp;

...
static final long MILI_SECONDS_PER_DAY = 86400000;

    /**
     * @param startDate initial date to start counting from
     * @param nDays number of days can be begative
     * @return a Date object nDays after startdate
     */
    public static Date dateAfterNDays( Date startDate, int nDays)
    {
       if (startDate == null)
           startDate = new Date(Date.getCurrentDate());  // assume today

       Timestamp ts = startDate.timestampValue();
       long nextDatesSecs = ts.getTime() + (MILI_SECONDS_PER_DAY * nDays);
       return new Date( new Timestamp(nextDatesSecs));
    }

Here is an other example that calculates the number of days between two dates. The parameter dates are oracle.jbo.domain.Date and the return type is an oracle.jbo.domain.Number.

    /**
     * return the number of days between two dates
     */

    private Number dateDifference(InDays Date startDate, Date endDate)
    {
       if (startDate == null)
           startDate = new Date(Date.getCurrentDate()); // assume today

       if (endDate == null)
            endDate = new Date(Date.getCurrentDate());  // asume today again

       Timestamp tsStart = startDate.timestampValue();
       Timestamp tsEnd = endDate.timestampValue();

       long ndays = (tsEnd.getTime() - tsStart.getTime()) / MILI_SECONDS_PER_DAY;

       return new Number(ndays);
    }

Monday, 26 February 2007

Selecting af:tableSelectOne by clicking anywhere in the table row

Just picked it up from the Oracle Java Developer Newsletter. It's sure worth it. By the way I just added Veniamin Goldin's Blog to my Oracle Links. He offers lots of goodies and unlike many others his tips tricks and examples are ready to be used