Dive into Oracle ADF

Send me a mail
 Dive into Oracle ADF   Click to see the XML version of this web page.   (Updated: 2/3/2008; 9:12:55 PM.)
Tips and tricks from Steve Muench on Oracle ADF Framework and JDeveloper IDE

Search blog with Google:
 

Search BC4J JavaDoc:
 

April 2004
Sun Mon Tue Wed Thu Fri Sat
        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  
Mar   May

Get Firefox!

Wednesday, April 07, 2004

I'm working today on finishing up the JavaDoc for the ADF Toy Store Demo. In the past, I might have to manually search each source code file to find out what methods were missing JavaDoc, or might have to "eyeball" each method and make sure that I correctly was using the JavaDoc @param, @return, and @throws attributes.

In JDeveloper 10g, we have a new feature called "Audit" which allows you to audit your code to make sure that it follows a set of rules. JDev 10g ships with an API for defining your own custom audit rules -- a forthcoming HowTo on OTN by the product manager responsible for this feature should shed more light on that for you -- but in the meantime the product ships with a number of very-useful built-in audit rule-sets, one of which is called "JavaDoc". By running the "JavaDoc" audit on your project or workspace, you can have JDeveloper do all the hard work of finding where in your code you are missing JavaDoc or have implemented your JavaDoc incorrectly. Just like compilation errors, a log window pane appears showing you all the violations that you need to go fix, and as you are fixing them you can click the "Refresh" right mouse menu to  update the list and see what's still left to fix.

On any rule violation in the list, you can select the right-mouse menu and get options that help you understand the violation and in some cases directly apply the fix to your code. For example, on one I see:

  • Apply "Add Tag"  (to fix the problem)
  • About "Missing Required Tag" Rule...
  • Hide "Missing Require Tag" Violations

There's also a menu pick at the top to "Apply Default Fixes" which I presume will fix all fixable audit problem automatically for me.

As if that weren't already a huge help, we also have a new feature called "Add JavaDoc Comments..." that's available on the right-mouse menu in the code editor so when you jump to a method that is missing JavaDoc, you just click on the method name and pick "Add JavaDoc Comments..." and correctly-formatted javadoc gets added for you. If you have forgotten a parameter name, spelled a parameter name wrong, or gotten the order of the parameters wrong, the combination of the JavaDoc audit plus the Add JavaDoc Comments... command can make quick work of fixing the problem. If you already have some JavaDoc and you pick "Add JavaDoc Comments..." it incrementally just adds the bits that are missing for you, leaving your existing comments in place.

To help you get in the habit of writing good JavaDoc comments from the start, when you begin to type a method comment with a "/**" then press [Enter] to go to the new line, JDeveloper automatically generates your JavaDoc comments with appropriate @param, @return, and @throws clauses for you. Of course, the developers needs to fill in the human-readable description of what the parameter means and what the method does, but all of the drudge work is done for you.

You can control exactly what the Add JavaDoc Comments related features add to your JavaDoc by visiting the:

Tools | Preferences... | Code Editor > Java > Javadoc

preference panel. It's really quite powerful and I can tell you first hand how much of a time-saver it is proving for me!

If you find yourself picking this right-mouse "Add JavaDoc Comments..." option many times, it might be time well spent to define a keyboard accelerator for it so you don't have to have your hands leave the keyboard to invoke the functionality. To do this, visit the:

Tools | Preferences... | Accelerators

preference page, select "Add JavaDoc Comments" in the "Actions:" list, and click into the "New Accelerator" field. Type some key combination like [Ctrl]-[Shift]-J or something, and then click (Ok). Now just press [Ctrl]-[Shift]-J will make the functionality kick in instead of having to use the mouse.

Last, but not least, once your code has JavaDoc comments as it should, anytime you click on a method in your code and want a refresher on what that method does and what it's parameters mean, you can do a right-mouse "Quick JavaDoc" and see a targeted JavaDoc entry in a floating window above your code that disappears after you've consulted it and gotten the information you need.


12:06:32 PM    



Here's a tip on something that I do many times a day in JDeveloper. I think of a method name in the current class I'm working on. Of course I could use the "incremental search forward" functionality (Ctrl-E by default) to search for the method, however, more often than not it's a method that both is defined and used in the current class, so I end up finding multiple places in the source code that match that name. To quickly navigate to the method declaration, I do the following:

  • Press [Ctrl]-[Shift]-S to move focus to the structure pane
  • Start typing the method name, and the structure pane's incremental search finds it in the list of class members in the structure pane
  • If there are multiple overloads, I use the arrow keys to pick the one I want
  • Then, I press [Enter] and press [Enter] a second time.

This jumps me to the method I'm looking for without having to have my hands leave the keyboard.


11:38:58 AM    


Simone writes in to ask, "Given two view object instances at runtime, how can I found out the names of the attributes involved in the viewlink that links them?"

The following method illustrates how you might accomplish this for two view object instances that are connected in the application module's data model by a view link instance. This means that in the "Data Model" panel of the application module at design time, the detail would appear indented beneath the master, indicating an active coordination of master/detail relationship at runtime when the master rowset current-row changes. The method would work inside any ApplicationModuleImpl class...

  protected void viewlinkInfoFor(ViewObject source, ViewObject target) {
    String[] sourceNames = source.getViewLinkNames();
    System.out.println("Source & Target Involved in View Links:");
    for (int z=0,num=(sourceNames!=null?sourceNames.length:0;z<num;z++) {
      ViewLink vl = findViewLink(sourceNames[z]);
      if (vl.getDestination() == target) {
        System.out.println("ViewLink instance named '"+vl.getName()+"'");
        ViewLinkDefImpl vlDef = ViewLinkDefImpl.findDefObject(vl.getDefFullName());
        AttributeDef[] sourceAttributes = vlDef.getAttributeDefImpls();
        AttributeDef[] targetAttributes = vlDef.getOtherAttributeDefImpls();
        for (int y = 0; y < sourceAttributes.length; y++) {
          System.out.println("-> "+
                             source.getName()
                             +"."+
                             sourceAttributes[y].getName()
                             +"="+
                             target.getName()
                             +"."+
                             targetAttributes[y].getName());       
        }
      }
    }   
  }

11:27:00 AM    


This tip will be in the JDeveloper 10g (v9.0.5.1) production release notes, but for higher visibility/searchability, I'm posting it here as well.

Default Iterators for View Object Rowsets Advance to First Row When Bound to ADF Iterator Bindings

The new ADF iterator bindings in JDeveloper 10g cause the iterator to which they are bound to advance to the first row in the rowset. This is the optimal behavior for the UI rendering of those rows, and the behavior is required for ADF iterator bindings to work correctly with standard JSP tag libraries like JSTL.

However, this new behavior can cause trouble for existing application logic when you migrate a BC4J application to JDeveloper 10g and begin to add new ADF iterator bindings to your application. Consider the following hypothetical application module method representing some user-written business logic.

public boolean employeeExists(Number empno) {
  EmpViewImpl eview = getEmpView();
  eview.setWhereClause("empno = :1");
  eview.setWhereClauseParam(0,empno);
  eview.executeQuery();
  /*
   * MIGRATION CAVEAT
   * ~~~~~~~~~~~~~~~~
   * When no ADF iterator bindings are bound to the "EmpView"
   * view object instance -- more precisely, to the default iterator
   * of its default rowset -- then immediately after executeQuery()
   * the iterator will be at the slot before the first row. Assuming
   * this query retrives a single row, then the eview.hasNext() will
   * be true, since we haven't yet advanced to the first row.
   *
   * When an ADF iterator binding is bound to "EmpView" then after
   * the executeQuery() the iterator will advance to sit on the first
   * row of the result -- in this case, the only row in the result --
   * and the eview.hasNext() will return false.
   */
  if (eview.hasNext()) {
    return true;
  }
  else {
    return false;
  }
}

The issue can also surface in middle-tier business logic which is written to loop over rowset results and perform some operation on each row. Examples include calculating the sum of some numeric attribute in each row, but not limited to this. For example, you might have code like:

public Number shoppingCartTotal() {
  ShoppingCartImpl cart = getShoppingCart();
  cart.reset();
  double total = 0;
  /*
   * MIGRATION CAVEAT
   * ~~~~~~~~~~~~~~~~
   * When no ADF iterator bindings are bound to the "ShoppingCart"
   * view object instance, then immediately after the reset() call above
   * the iterator will be at the slot before the first row. The loop
   * below will operate over all N rows in the rowset coded like this.
   *
   * When an ADF iterator binding is bound to "ShoppingCart" then after
   * the reset() call, the iterator will advance to sit on the first
   * row of the result. If the loop code is not changed, then it will
   * operate on the 2nd through the Nth rows, missing the first row.
   */
  while (cart.hasNext()) {
    ShoppingCartRowImpl curCartItem = (ShoppingCartRowImpl)cart.next();
    total += curCartItem.getExtendedTotal().doubleValue();
  }
  return total;
}

There are two basic solutions to the issue:

  1. Where just testing whether the first row exists, use the first() API and test whether it is null or not.
  2. Where performing iteration over the rowset, use the createRowSetIterator() API to create a secondary iterator for use in middle-tier programmatic business logic instead. Remember to call closeRowSetIterator() on the iterator when you are finished with the loop if you do not want the iterator to remain around and active.

Use these best-practices for new code that you write as well. Following the two pieces of advice above, the two examples illustrated above would be rewritten like:

public boolean employeeExists(Number empno) {
  EmpViewImpl eview = getEmpView();
  eview.setWhereClause("empno = :1");
  eview.setWhereClauseParam(0,empno);
  eview.executeQuery();
  /*
   * Using first() instead of hasNext() to test for existence of
   * at least one row in the result since default iterator might be
   * bound to an ADF iterator binding being used in the view layer
   */
  if (eview.first() != null) { /* Using first() instead of hasNext() */
    return true;
  }
  else {
    return false;
  }
}

and

public double shoppingCartTotal() {
  ShoppingCartImpl cart = getShoppingCart();
  /*
   * Using secondary iterator since default iterator
   * might be bound to an ADF iterator binding being used
   * in the view layer
   */
  RowSetIterator cartIter = cart.createRowSetIterator(null);
  double total = 0;
  while (cartIter.hasNext()) {
    ShoppingCartRowImpl curCartItem = (ShoppingCartRowImpl)cartIter.next();
    total += curCartItem.getExtendedTotal().doubleValue();
  }
  cartIter.closeRowSetIterator();
  return total;
}

I ran into the second issue above when migrating the BC4J Toy Store application to use the new ADF Binding Layer as part of the ADF Toy Store. As suggested above, I changed the code to use a secondary iterator and all was back working again.


1:04:03 AM    


© Copyright 2008 Steve Muench.