Friday Apr 19, 2013

Lions and Tigers and RangeSize, Oh My!

One of the most common mistakes I see made in ADF Business Components based applications is a failure to tune the View Objects, and specifically to tune the in Batches of parameter in the VO tuning section. This setting defaults to 1, which does not always meet the needs of the consuming UI or service interface and should generally be changed.  This is a topic that I and others have covered before.

Now certainly the batch fetch size is a crucial tuning parameter as it controls how many times the framework has to hit the database to get the rows needed to service a particular function.  However, in this posting I wanted to turn my attention to another factor which can also have a significant effect - the iterator RangeSize.

The background to this was some recent detective work on an application where the time taken to display one particular screen was suspiciously long. 
The page in question had a tabular display of data, but an inspection of the VO tuning parameters showed that a reasonable Batch size of 51 was being used. What's more the As Needed switch rather than the All at Once option in the VO tuning was being used. So the developer had done totally the right things there.
Running a SQL Trace on the page revealed an interesting thing though.  Because the batch size was pretty high we'd expect that the framework would have to only do one or at most two fetches from the cursor to satisfy the needs of that table.  However The TKProf output showed that in fact over 150 fetches took place retrieving over 8000 rows!

My thought processes in diagnosing this were to look in the following places:
  1. Are there alternative VO Instances defined on the AM where the tuning parameters are different (e.g. ALL_ROWS was specified)? We know the base definition is OK but it could be overridden.  
  2. Any programmatic calls to change the Batch Size or fetch node in the VO?
  3. Any programatic calls to last() on the rowset or iterations through the rowset? 
  4. Check for a RangeSize of -1 on the iterator definition in the pageDef files.

All of these drew a blank.  The last one in particular felt like the problem but a search for the value of -1 in the pageDefs of the UI project only turned up legitimate usages of the -1 value. 

Hold on I don't Understand This RangeSize?

Maybe I should take a quick step back and explain the iterator RangeSize.  So, as we've seen, the tuning options in the view Object will control how often the VO has to go back to the database to get a specific number of rows. The iterator rangeSize is defined in the pageDef file for a particular page, fragment or method activity and it defines how many rows the UI layer should ask the service layer (in this case the VO) for. 
Here's a typical definition that you'll see in the pageDef:
<iterator Binds="EmployeesView1" 
          RangeSize="25" 
          DataControl="HRServiceAMDataControl" 
          id="EmployeesView1Iterator"
          ChangeEventPolicy="ppr"/>

You'll see that the rangeSize here is set to 25 which just happens to be the value that is defaulted in when you drag and drop a binding into the page.  However, it turns out that 25 is not the default value, something which has a bearing later in this investigation as we'll see.
So in this default case when the iterator is asked for data, it in turn will ask the VO for 25 rows, and if the VO does not already have that many rows in the cache it will have to go back to the database as many times, as determined by the batch-size, as it needs to get enough data.

Back to the Problem page 


As it happens the pageDef for the table displaying the problem VO was indeed the defacto default of 25, so, sad to say, it was not the obvious suspect at fault, more investigation was needed.

At this stage the investigation splits into a couple of parallel efforts, manual code inspection, and tracing using the ADF Logging capability to try and work out what interactions were happening with the problem VO.

Welcome to the Internet, Please Tread with Care

What can we trust? Well in the world of ADF Blogs in the wild there are some great bloggers, but that does not mean that you can just copy without thinking.  It turned out that one of the problems with this application was to fall foul of copy-without-thinking syndrome.
The code in question seems innocent enough, it's published out there on the internet as a way of refreshing an iterator:

//Please, Please Don't do this! (My comment)
DCIteratorBinding iterBind= (DCIteratorBinding)dc.get("<your iterator name>");  
iterBind.refresh(DCIteratorBinding.RANGESIZE_UNLIMITED);  

Two facts to discuss in relation to this code snippet:
  1. Read the JavaDoc for DCIteratorBinding - the refresh() method is very clearly marked as Internal only, applications should not use. That's what we call "a hint" in the trade, you can choose to ignore it but don't come crying...
  2. Look at that parameter being passed to the refresh method DCIteratorBinding.RANGESIZE_UNLIMITED - can you guess what that does? Yes it does the same as setting the RangeSize to -1 and will cause the VO to be asked for all of the data for that query. You can see how bad that could be if the VO has the potential to return a lot of rows.
So something to put right already. 

But Wait, There's more!

Although the call to refresh was a great catch and the application will be better without it, it turned out not to be the cause - darn. 
However, the parellel effort to run some more tracing found the smoking gun.  The ADF Log trace showed a double execute on the iterator for the VO in question, or to be more precise, executes on two different iterators bound to the same VO from different regions on the page. 
A useful diagnostic here was then to set a breakpoint in the setRangeSize() of oracle.adf.model.binding.DCIteratorBinding.  Doing this we could see that the first iterator execution was actually responsible for setting the RangeSize to -1 and the second to the value we where expecting for that table based on the pageDef. 
All credit to the development team I was working with here who ferreted out the actual problem, it was finally down to one of omission. 

Recall I made the statement earlier about 25 being the defacto default for the RangeSize? Very true, when you create a binding, that's what the IDE puts in for you.  But what's the actual default? Well that turns out to be -1.  So if you omit the RangeSize from the iterator definition by intent, or mistake, you're going to have a potential side effect you may not have expected!  That was exactly the problem in this case  - no RangeSize. 

Specifically the problem was caused by a method binding in one of the bounded task flows in the page. The pageDef for this method activity included and iterator defined for the problem VO but without a RangeSize defined.  

Lessons to Learn

  1. For each peice of UI that is bound to data understand how the user is going to use that screen - will they view just one record, will they scroll through several or a lot, will they only scroll downwards and process one record at a time and so on. You can't effectivly tune a page until you understand how it will be used.
  2. Always tune your VO definitions or instances to the requirements of each endpoint.  If this means having multiple VO definitions or multiple VO instances with different tuning parameters then do it.
  3. If you have consciously set your Batch size in the VO tuning parameters to a small number because that's all you see on the page at once then also tune the pageDef iterator RangeSize. Otherwise that defacto 25 RangeSize could generate a bunch of unnecessary calls back to the database. Think about it, if you have a single record form and you've not tuned the VO Batch size and you've not tuned the iterator RangeSize, you could be doing 25 roundtrips to the database just to see one record.  Great way to please your DBA!
  4. Use a RangeSize of -1 with caution.  It has its place for iterators that serve short lists used for lookups and menus but anything else is an exception.
  5. Don't blindly copy everything you see in the blog-o-sphere. If you don't recognize an API call then look it up.  If something says it's for internal use only, then guess what, don't use it.
  6. Never ever, define an iterator in your pageDef without an explicit rangeSize. If you need to see all rows, say so with the -1 value, otherwise use a positive integer.

We all go home older and wiser...


Monday Apr 15, 2013

Building the revealPanel - Part 2

In the first part of this series I described the basic physical makeup of the revealPanel and described a simple server side approach to the display and hide of the information panel and indicator triangle.  

In this posting I want to look at an alternative approach that uses the same physical components in the UI, but rather than handling the reveal with a roundtrip to the server, does it all in the browser itself using JavaScript.

The sample that you can download shows both approaches. 

Changing the Configuration for JavaScript Use

The JavaScript version of the component works in essentially the same way as the server side version that I talked about last time, that is, it simply sets the visible attribute of the panelGroupLayouts that contain the reveal information and the triangle. The good thing is at this is a property that we can set through the ADF Faces client component JavaScript API and we never really have to tell the server side what the state of the panels is as we'll always revert to the all-closed layout if anything like navigation away and back takes place. 

So the definition of say the triangle PGL simply has the visible attribute set to a hardcoded value of false, with no need to refer to a managed bean. Of course we need to hold the information about which panels are expanded somewhere,  so that we can close one set as another is opened, or close a set if it is currently open. To do this I actually create a new JavaScript object at the page level called revealPanelGroupMap (if you want to read along open the revealPanel.js file in the demo). 

This Object does not directly hold the information about the panel, it's a slightly more complex structure:

  1. revealPanelGroupMap is a Map (thinking in Java terms) of revealGroup Objects 
  2. Each RevealGroup contains an array of Topics and the currently selected Topic in the array
  3. Each Topic contains component references to the triangle PGL and the revealPanel PGL as well as the topic number
The fact that revealPanelGroupMap can hold multiple revealGroups allows us to host multiple revealPanels on the same page as long as each is identified by a unique string which is used to key that revealPanelGroupMap.

How are the Data Structures Created? 

So this revealPanelGroupMap does not appear out of thin air, but neither in fact is it created in advance in any way. Because, for an individual page, you may have dynamic regions and suchlike, I wanted the revealPanels to be self registering and created on demand rather than pre-defined in some way.  The way that I ended up achieving this was to add a client attribute to each of the three important panelGroupLayouts (the topic, the triangle and the revealPanel). This locator is just a string consisting of 3 parts delimited by ":".

  1. The panelGroupId - e.g. "REVEAL1"
  2. The TopicID - zero indexed
  3. The SegmentID - each topic has three segments 0-2 where 0 is the topic, 1 is the triangle and 2 is the revealPanel

So the client attribute definition in the page looks like this:

<af:clientAttribute name="revealPanelLocator" value="REVEAL1:0:0"/>

This indicates that this panel is the topic panel for the first topic in the REVEAL1 group. 

When the first of the topics is clicked within a revealPanel, the click handler code grabs this locator and parses out the segments.  From the first segment it can tell if this group has been seen before, if it has then great, it can start to set visibility etc. If, however, the group is not present in the main map then we do a little self discovery to find out how many Topics there are in the revealPanel group as a whole and store the relevant component references.

Walking the Tree

So how to we self discover the group?  Well the first bit is easy.  Using the getParent() API  from the component that raised the click event we can get a reference to the hosting panelGridLayout.  

I then use the visitChildren() api on the grid to visit each of its top level children. Each of these will actually be one of those segment panelGroupLayouts. So in a panelGrid with 9 children that will actually define 3 topics each consisting of three panels (topic, triangle, reveal) .  As each of those panels has an associated clientAttribute with it's locator we can add it to the correct place in the revealGroup control structure. 

The visitChildren API actually defines a callback function that the framework will call for you, passing any context that you've defined and the child component it has found. 

Note, it would be perfectly possible to alter the sample code to do away with the clientAttribute all together. I actually wrote it this way to allow for more than one revealPanel to be hosted within the same physical panelGrid. However, of that's not something you need then the page definition could be condensed down to simply the clientListener in the Topic panel.  Left as an exercise for the reader... 

Acting on the Click

Once the data structure is build for a particular revealPanel it will be caching the component references to the triangle and to the reveal PGL so you can simply call setVisible(true|false) to manage the hide and display.

You've Noticed the Fatal Flaw?

Well I hope so... If I'm storing the component references to the various panelGroupLayouts will they stay valid? Well certainly if I navigate off to another page no.  But that's OK, because if I navigate away then the document object in the DOM will have been destroyed, taking my data structures with it. So when I re-enter the page they just get re-built.  So no problems there.   However, if my page contains a region and one of the fragments on the region has a revealPanel then there is a problem.  Changing the view in the region will not effect the overall document so the control structures contain references to components that have been destroyed - if I go away and come back to a revealGroup on a fragment then the references will be bad.  However,  there is a simple solution to this.  The client side components have an isDead() API call which we can use to check to see if the reference held in the cache is still valid.  If it is then great. If not we assume that we need to rebuild the entire structure for that revealPanel.

Adding Some Bling with Animations

I promised some animation as part of this version and indeed it's in there.  So in fact, rather than simply setting the visible attribute when the topic is clicked I do that, but I also apply some CSS styles as well.  

I'm essentially re-using some of the things I've discussed before in my postings on animations, using in this case Scale transforms for the main reveal panel and a color transform for the triangle.

Here's the logical flow:

Revealing a Topic

  1. Set the triangle to visible - it's initial style will define it as transparent
  2. Set the reveal panel as visible  - its initial style will define a scale of (1,0) with full width but no height
  3. Set the reveal panel style to revealPanelAnimated. This resets the scale to (1,1) - full size over about 200ms
  4. Register a listener on the animation
  5. When the animation is finished then reset the style on the triangle to restore its color.

Note that I've used a scale transform here rather than actually trying to move the PGL <div> around the screen. Trying the latter could put you into conflict with the existing layout management and will certainly cause some problems with z-order in the DOM so it's better to avoid the issue by scaling in place.  It gives a pleasing effect.  

The actual code and styles involved in all of this is a little too complex to reproduce here in the blog, so do be sure to check out the demo.  I've added plenty of comments to help you along.  

Friday Apr 12, 2013

Building the revealPanel - Part 1

Working with one of our internal teams the other day, a couple of interesting ideas came up for presenting information. One of these was a sort of twist on panelAccordion and it seems to be a nice usecase to work through to show how you can get creative with your UI without having to ask for and wait for new components. 

The Brief

As I said, the requirement here was to produce something like panelAccordion. However, rather than restricting the selection bar to just a single line of text an maybe an icon,  what if you wanted larger images or multiple lines?  Here's a mockup:



Then like a panelAccordion, with discloseMany="false" and discloseNone="true" you want to be able to expand the details of up to one and only one of the selection panels (I'll call them "Topics" from now on).



So if I click anywhere in the first topic bar in the above image then that topic will collapse, if I click on a different topic bar then the first will collapse and the new selection will expand.
You'll notice the modernist conceit of the little triangle linking the revealed panel with it's topic. It's a small thing but that also helps to polish the look and make it feel current.

The UI Elements

So how would we build this? Well the answer is that the UI itself is surprisingly easy. That's all thanks to our new friend, the panelGridLayout  which hands us everything we need on a plate. 
We can break down the mockup above into three sets of repeating units, remember I called them topics.  Each topic is identical, so let's just look at one.
Logically the topic is composed of three vertically arranged areas:

(1) The topic header itself
        |
(2) The little indicator triangle
        |
(3) The reveal panel area with the additional content 

Area 1, the header is always displayed and areas 2 and 3 we need to reveal or hide dynamically when the user clicks anywhere on area 1.
In panelGridLayout terms we can simply translate this into three grid rows, one for each area.  The key point here is that if you set the height attribute of a <af:gridRow> to the value "auto" the grid will automatically size that row to fit its children, so guess what: if we set visible="false" on the contents in areas 2 and 3 then the grid shrinks for free. I just love that this is so easy.
So here's the outline component model for a revealPanel (Yes there is a full demo you can download - read on to the end)

<panelGridLayout>
<!-- For Each Topic -->
  <gridRow height="50px">
    <gridCell columnSpan="3" halign="stretch">
      <panelGroupLayout layout="vertical" styleClass="revealTopicPanel">
      <!-- Topic header content will go here --> 
      <panelGroupLayout>
    </gridCell>
  </gridRow>
  <gridRow height="auto">
    <gridCell width="80px"/>
    <gridCell width="10px" valign="bottom" >
      <panelGroupLayout layout="vertical" 
                        styleClass="triangleMarker"/>
    </gridCell>
    <gridCell width="100%"/>
  </gridRow>
  <gridRow height="auto">
    <gridCell columnSpan="3" halign="stretch">
      <panelGroupLayout styleClass="revealPanel"
      <!-- Revealed content will go here --> 
    </gridCell>
  </gridRow>
</panelGridLayout>

Let's look at the key points
  1. The first grid row representing the topic header can be fixed in size to make sure that the unexpanded list is regular.
  2. The first grid row just contains a single grid cell that streches across three logical cells using the columnSpan attribute. The reason for doing this will become clear when we look at the second area of the topic 
  3. We use halign="stretch" on that first area gridCell to fill it with its content. This is important to make the included component (e.g. panelGroupLayout in this case) fill the cell with its styling.
  4. Moving onto the second area of the topic than contains the little indicator triangle, here the row as it's height as auto so it will collapse when we hide the triangle.
  5. The triangle area row is divided into three cells. The center cell contains the actual triangle (see below) and the other two cells are used to position the triangle cell to a particular horizontal position. In this case I'm indenting the triangle by a fixed 80 pixels, however, if I adjusted the first and last cells to use width="50%" then the triangle would be centered to the width of the panel.
  6. The final area is again a grid row with height set to auto containing a single cell with columnSpan set to 3 and stretching it's content. 

The Triangle

The little triangle indicator could be done using an image file, however, here I'm just using a CSS style which is simpler and enables you to change the color as required. Here's the definition:

 .triangleMarker {
  width:0px;
  height:0px;
  border-left:8px solid transparent;
  border-right:8px solid transparent;
  border-bottom:8px solid rgb(247,255,214);  
}
This style is applied to the vertical panelGridLayout in the center area.  Vertical panelGrids become simple html <div> elements.

Managing the Topic - The Server Side Version

I'll cover a more advanced version of the component in my next posting which uses JavaScript to manage the hide and reveal of the panels, but in this post, let's look at the simple version. 
As discussed, the paneLGridLayout actually does all of the UI resizing for us, so all we really need to do to manage the set of topics is two things:
  1. Introduce a management class that will tell the reveal panel and triangle panel if they should be visible or not
  2. A small amount of event code to translate a click anywhere on the topic panel into a change in the above management class. 

The Management Class

For the sake of the demo I've developed a very simple management class that does not attempt to do anything fancy such as handling multiple sets of revealPanels (that task is left for the JavaScript implementation). Here's the class:

package oracle.demo.view;
import java.util.ArrayList;
import java.util.List;
public class RevealPanelManager {
    private int _panelCount = 10;
    private int _toggleTarget = -1;
    private List<Boolean> _revealedList;

    /**
     * Switches the state of the currently selected panel 
     */
    public void toggleState() {
        if (_toggleTarget >= 0 && _panelCount > 0) {
            boolean currentState = false;
            if (_revealedList != null){
                currentState = _revealedList.get(_toggleTarget);
            }
            resetStates();
            if (!currentState) {
                _revealedList.set(_toggleTarget, true);
            }
            _toggleTarget = -1;
        }
    }
    /**
     * Used to inject a panelCount into the management structure. If not called then an array upper limit of 
     * 10 will be used
     * @param panelCount
     */
    public void setPanelCount(Long panelCount) {
        int candidateCount = panelCount.intValue();
        if (candidateCount > 0) {
            //reset the list & re-create in the new size
            _revealedList = null;
            _panelCount = candidateCount;
            resetStates();    
        }
    }
    /**
     * Invoked, probably from a setPropertyListener / setActionListener to set the id of the 
     * panel to act on.  This may disclose or hide depending on the current state of the selected
     * panel
     * @param toggleNo - index number of the panel
     */
    public void setToggleTarget(int toggleNo) {
        this._toggleTarget = toggleNo;
    }
    /**
     *Called by the panel to see if it should be visible or not
     * @return List of Booleans indexed by panel number
     */
    public List<Boolean> getRevealed() {
        return _revealedList;
    }
    /**
     * Either creates or reinitializes the array to close all the 
     * panels
     */
    private void resetStates() {
        if (_revealedList == null) {
            _revealedList = new ArrayList<Boolean>(_panelCount);
            for (int i=0; i < _panelCount; i++) {
              _revealedList.add(i,false);
            }
        }
        else{
            for (int i = 0; i < _panelCount; i++) {
                _revealedList.set(i, false);
            }            
        }
    }    
}

As I said this class only manages a single set of topics in a single grid and it defined as a managed bean in your page flow definition for the relevant view something like this:

  <managed-bean>
    <managed-bean-name>revealManager</managed-bean-name>
    <managed-bean-class>oracle.demo.view.RevealPanelManager</managed-bean-class>
    <managed-bean-scope>view</managed-bean-scope>
    <managed-property>
      <property-name>panelCount</property-name>
      <property-class>java.lang.Long</property-class>
      <value>3</value>
    </managed-property>
  </managed-bean>
Notice how the expected panel count (3) in this case  is injected into this bean.

Once this management bean is defined then the content that we want to hide and display dynamically (e.g. the triangle panelGrouplayout and the revealPanel panelGrouplayout) and both use expression language to determine if they should be visible. e.g. 

<af:panelGroupLayout layout="vertical" 
                     styleClass="triangleMarker"
                     visible="#{viewScope.revealManager.revealed[0]}"/>
Where the zero based index number is passed into the list evaluation. 

The Click Event Handler

The final thing we need to do is to find a way to call the toggleState() method in the revealManager. The only twist here is that we don't want to have a commandButton or link in the panel to click. We want to be able to click anywhere on the panel.
This is a well established technique so you can look at the demo for the details of the code, but basically we define a small JavaScript function which is registered with the client side click. When the user clicks, this reads the numerical ID of the panel that was selected and makes a call back to the server to the handlePanelToggle() server listener method. That then talks to the revealManager, setting the id of the panel to toggle and invoking the toggle, finishing off with a partialUpdate to get the whole grid to re-draw. Have a look at the RevealRowComponentHandler.java class in the demo for the details.  

And the Result

Well here's the ADF implementation of the revealPanel. First closed:


And now opened:



The Demo

I've uploaded a demo that shows the basic version of revealPanel discussed here and a more advanced version which is discussed in the next article.  You can download the sample from the ADF Code Samples Repository

This demo is not foolproof and at the moment the animations are restricted to Chrome and Safari (WebKit browsers) It also needs to check for older browser versions that do not support the animations and switch them off

Note: The sample is written in 11.1.1.7 it will not work in 11.1.1.6 or older.

Further Note: There seems to have been a slight change in the js API in 11.1.2.n which are breaking the code at the moment - working on that one

Tuesday Apr 02, 2013

panelGridLayout - now we are complete

Good news, with the arrival of 11.1.1.7 (Patchset 6) the immensely useful <af:panelGridLayout> component has made it into the 11.1.1.n code-line.

If you're not familiar with panelGridLayout then check out my article on the subject from earlier and then go and check out the demo page which will link you off to the documentation etc. 

Friday Mar 01, 2013

I Did It Because I Can

Somewhat inspired by Chris Muir's Record breaking (i.e. he claims to be the first) on getting ADF running on a Raspberry Pi,  I just had to do it.. Yes Husdon Continuous Integration Server running on a Model B using Java 7 and  Jetty as the application server. Imagine it now... - a supercluster of $35 computers doing your continuous builds (reallllllly sloooowly). But the fact remains, it ran, and I claim that record:

Now I just have to smuggle the machine back to my son's room before he gets back from school... 

Thursday Feb 21, 2013

Get More From Your Messages

Within ADF Faces we take much goodness and added value for granted.  One such feature came to my attention just the other day. Had you noticed that when you have several errors outstanding on the screen the framework gives you a hyperlink to set focus to that field? See the error on commissionPct below and the hyperlink that you could click to move focus to it in the screen:

 So that's neat and I must confess that although I must have stared this one in the face hundreds of times I never really groked how cool that was. 

Anyway, as is usual, this was not just a random act of attention on my part but rather considering a couple of different user cases which both boiled down to the basic question.  Can we extend these error dialogs in some way to show more detail or carry out some other action in response to the error? So that's what I wanted to work through in this article, because the answer is (of course) yes!

How to make the Messages Interactive?

So let's take a look at how to do this. The clue is in the documentation for the tag which casually mentions that  you can include HTML into your Faces messages or message detail. and one of the examples is the embedding of an anchor tag to embed a hyperlink - something that could indeed be useful in a message.  So it occurred to me that this hyperlink provided the opportunity for  an actionable gesture that the user could make to do stuff within the actual application as well as jump off to another one. However, it's not all plain sailing, you can't just embed any old HTML markup into the messages (I did try!) So no embedding of buttons, and even for an <a> tag you can't wire in JavaScript with an onClick=, that all get stripped out.

However, with a carefully shaped href attribute you can call JavaScript. So for example I've crafted the following message detail string:

<html>Detail for errror <a href=javascript:errorDetailCallback('it1','d1');>here</a></html>

 This renders the word "here" as a link in the message and then when the user clicks on that it calls a JavaScript errorDetailCallback() with a couple of arguments.  This function is included in a script linked onto the page using a standard <af:resource> tag.

The script itself could do anything. In my sample I'm actually making it call back to the server (via an <af:serverListener> defined in the document. Here's the JavaScript function:

 function errorDetailCallback(errorContext, documentComponentId){
    
    var callbackComponent = AdfPage.PAGE.findComponentByAbsoluteId(documentComponentId);
    if (callbackComponent != null) {
        var serverEvent = new AdfCustomEvent(callbackComponent, 
                                             "errorDetailEvent",
                                             {detailKey : errorContext},
                                             true);
        serverEvent.queue(true);
    }
    else{
        AdfLogger.LOGGER.severe("errorDetailCallback: Error unable to locate document component: " 
                                + documentComponentId);
    }  
}

The custom event is wired into the server via the <af:serverListener> tag:

<af:serverListener type="errorDetailEvent" 
                   method="#{indexPageBB.errorDrilldownHandler}"/>

 The server side method (errorDrilldownHandler()) in my case gets the custom event and then shows a popup in response:

 public void errorDrilldownHandler(ClientEvent clientEvent) {
   Map params = clientEvent.getParameters();
   String detailKey = (String)params.get("detailKey");
   if (detailKey != null && _errorDetail.containsKey(detailKey)){
     _lastErrorKey = detailKey;
   }
   else{
     _lastErrorKey = "UNKNOWN";
   }
        
   //Now do what you want. In this case show a popup with a detail message
   RichPopup.PopupHints hints = new RichPopup.PopupHints();
   getDetailPopup().show(hints);
 }

Note that in the above case, the _lastErrorKey  is simply a variable which is used by the getter that populates the text in the popup, it's used as a key into a Map with some further message details in it. However, what you do in this callback routine is of course up to you. 

Wednesday Feb 20, 2013

Using JDeveloper 11.1.1.6 & 11.1.2.3 with Subversion 1.7

This issue has come up a couple of times this week and has also be raised on the OTN JDeveloper Forum, so it seemed to be worth a quick public-service announcement.  

Subversion 1.7 introduces changes (AKA complete re-write) into the way that working copies are stored, the implication of this is that a 1.6 client and a 1.7 client cannot operate again the same working copy on a developer's workstation. 

Current versions of JDeveloper's SVN support contain 1.6 client code, so if you are mixing and matching JDeveloper and SVN / Tortoise command line operations you will run into trouble. 

e.g. The following sequence of operations will fail:

  1. Checkout working copy from the command line with the svn 1.7 client
  2. Make updates and attempt to checkin from JDeveloper 

However, the following should work:

  1. Checkout working copy from JDeveloper 
  2. Make updates and checkin from JDeveloper 

As will:

  1. Checkout working copy from the command line with the svn 1.7 client
  2. Disable the JDeveloper SVN plugin - (Versioning -> Configure -> Uncheck - Versioning Support for Subversion )
  3. Make updates  to existing files only from JDeveloper
  4. Commit / Add / Delete operations from Command line svn 1.7 client. 

Obviously, for convenience and because some operations such as refactoring need to issue SVN commands I'd recommend that, if using a 1.7 repository, you carry out all operations including the initial checkout from within the IDE.  

As a general guidance if you are going to upgrade your repository (and my further advice would be not to upgrade at the moment if your primary client is JDeveloper) I'd recommend that you clean up any outstanding transactions before the upgrade, and then, once the server is up to 1.7, do a clean checkout from JDeveloper and stick to working within that environment.

Monday Feb 18, 2013

MySQL & ADF Business Components - Enum types

A quick guide to effectively mapping and representing MySQL enumeration types through ADF Business Components [Read More]

Friday Jan 18, 2013

Refresh Problems using Adaptive Bindings

In a previous article (Towards Ultra-Reusability for ADF - Adaptive Bindings)  I discussed how ADF Data Binding can be a lot more flexible that you would think due to the neat trick of being able to use expression language within the PageDef file to create bind sources that  can be switched at runtime.

As it happens my good buddy Frank Nimphius picked up the technique to use on a particular  project and hit a slight problem. If you change the results of the EL used in the binding - for example, you switch the base VO from Departments to Employees, things don't get refreshed correctly.  In fact what you see is that any attribute names that happen to match between the old and the new source will be correctly populated with the new data but the actual list of attributes will not be refreshed. The effect is that if you were using one of these bindings to populate a table, the "shape" of the table, in terms of its columns, would not change. 

No worries though, given that Frank is a clever chap he worked out the correct way to address this which is to simply call clearForRecreate() on the iterator binding.

 BindingContext bctx = BindingContext.getCurrent();
 BindingContainer bindings = bctx.getCurrentBindingsEntry();
 DCIteratorBinding iter = (DCIteratorBinding)
 bindings.get("TableSourceIterator");
 iter.clearForRecreate();

Thanks Frank! 

Thursday Dec 20, 2012

Dude, how big's my browser?

It's sometimes funny how the same question comes from several unrelated places at almost the same time. I've had just such a question from a couple of unrelated projects recently, the query being the simple one of "how big is my browser window"?

Now in many cases this is a null question. With the correct use of layout containers (and testing) you can build layouts in ADF which adapt, flow and stretch well as the user re-sizes the browser. However, there are circumstances, as was the case here, where you want more drastic changes based in the amount of space you have to play with.  For example you may choose to hide a column in a table if the screen is below a certain size.

Well this is certainly possible, but of course it comes at a price.  If you want to know when the browser window has been resized you have to tell the server, and that's a round trip. So to do this efficiently this is not totally trivial. However, to save you the trouble of thinking too hard, I've written a sample - it is Christmas after all and Christmas is all about giving. 

The sample can be downloaded from the ADF Samples site on Java.net.

 

It's all pretty well documented  so I won't explain the code line by line here, but needless to say there is a bit of JavaScript and a server side event handler to listen for events queued from that script. I use a configurable JavaScript timer to buffer the resize events and keep the number of notifications to the server to an acceptable level. 

Once you have the  sizing information from the client, of course it's up to you to decide what to do with it!

The sample is written in 11.1.1.6 and will work for 11.1.1.n and 11.1.2.n versions. 

About

Hawaii, Yes! Duncan has been around Oracle technology way too long but occasionally has interesting things to say. He works in the Development Tools Division at Oracle, but you guessed that right? In his spare time he contributes to the Hudson CI Server Project at Eclipse
Follow DuncanMills on Twitter

Note that comments on this blog are moderated so (1) There may be a delay before it gets published (2) I reserve the right to ignore silly questions and comment spam is not tolerated - it gets deleted so don't even bother, we all have better things to do with our lives.
However, don't be put off, I want to hear what you have to say!

Search

Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today