Wednesday, October 15, 2014

What is the Entity Cache?

The ADF model layer uses an Entity Cache to manage updates to the database. This feature is one of the more misunderstood areas of the ADF framework because it’s not something you need a real good understanding of to create an ADF application. Here is a simple way to think about how the entity cache works.

If you are familiar with how the database command line works you know that you can insert or update rows in the database and until you commit, no one else accessing the system will see your changes until you commit them.   You can update a row, insert a row, delete a row and then re-query the tables you’ve changed and you will see your changes but no one else will.   This relates to the isolation transactional property of the ACID (Atomicity, Consistency, Isolation, Durability)  features of modern databases.    You should not use the feature in a web application because you should never post data without immediately following up with a commit in the same request.   Since the request is coming from a web page if you were to post without committing you run the risk of never completing the commit and leaving the table with locked rows.   

In ADF this problem is solved using the Entity Cache.   We can think of the Entity cache as a work area containing pending changes but the changes do not hold a lock in the database.   The cache adds an additional layer between the database session and the pending changes.  These pending changes never get posted to the database until a commit is performed.   In addition when you run a entity based query, after executing the query,  the framework will look in the Entity cache to see if any of the rows returned have pending changes and will merge the pending changes with the results just retrieved.   This simulates the behavior of a post without a commit.   Only the specific user sees their own pending changes but it does this without ever posting the changes so no lock is created on the table (Assuming jbo.locking.mode is set to the default optimistic setting not pessimistic).

The entity cache allows one view object to display uncommitted rows that were updated in another view object (within the same application module) that is using one or more of the same entities.  It also allows changes to be propagated across these view objects without ever communicating with the database.  For example, if the user updates a UI field linked to one view object and somewhere else on the page, a different view object using that same entity is displaying that value,  All you need to do is refresh that other part of the page and you will see the pending change.

Every entity has its own entity cache.    The cache will contain rows to update, rows to add and rows to delete for a single table.    When it is time to commit, the framework simply has to look in the entity caches, post each entity change and then perform a single commit.


Because the rows were not locked, it is possible that another user or process could have updated the same row. The framework will compare the original values that are also stored in the entity cache with the values currently in the database and if they differ you will get an error "JBO-25014: Another user has changed the row with primary key oracle.jbo.Key". Also see yet-another-reason-for-jbo-25014.

There is a lot more to the entity cache but hopeful this simple explanation will help.  For more in-depth information on the Entity Cache See

What Happens at Runtime: When View Objects and Entity Objects Cooperate

And

Friday, August 29, 2014

Adding a regex QBE validator for numeric columns

I had a table containing a column that was a Number type.  If you tried to filter on a non numeric value a ConverterException would be thrown.   That was fixed by putting a Number converter on the filter input text to allow only numbers.   That worked except now you couldn't use the Query by Example (QBE) Search criteria Operators.  




These are the supported operators

Operator Description
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
AND And
OR Or




You can write a custom QBE adapter to fix this but that seemed like overkill.

To fix this I changed the filter to be an input text and added a  regular expression validator to allow these operators.  The pattern I used is below.   I didn't find a regex example for QBE Numeric filtering and I'm not a regex expert (is anyone?)  so it took a while to get the regex string just right.  Hopefully this will save someone some time.
Here's the pattern
^[ ]*[<>]?([<=]|[>=])?[ ]*[0-9]+[ ]?(?i)([ ]+AND[ ]+[<>]?[ ]*[0-9]*|[ ]+OR[ ]+[<>]?[ ]*[0-9]*)?[ ]*$


<af:column sortProperty="Count" filterable="true"     headerText="Count"  id="c6">
  
<f:facet name="filter">
         
<af:inputText  value="#{vs.filterCriteria.Count}" columns="8" id="Count">                                                 
            
<af:validateRegExp pattern="^[ ]*[<>]?([<=]|[>=])?[ ]*[0-9]+[ ]?(?i)([ ]+AND[ ]+[<>]?[ ]*[0-9]*|[ ]+OR[ ]+[<>]?[ ]*[0-9]*)?[ ]*$" 
                  messageDetailNoMatch="Please Enter Number Only"/>
        
</af:inputText>             
  
</f:facet>         
                            
    
<af:outputText value="#{row.Count}"    id="ot7" />      
 <af:column>

Examples of Things you can put in the filter
10
50
> 10
>10  AND <  50
> 10 and < 50
10 or 50

 If the column is a String type (even through it might contain numbers) you don't have this problem and wouldn't need to add a regex Validator. 



Wednesday, December 18, 2013

How to View Service Proxy Request/Response message in Jdeveloper

In order to view the XML SOAP envelope of an ADF service Proxy, add this line to your server startup and the XML will be dumped the jdeveloper console output.

-Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true 
  In Jdeveloper, the server startup command line is accessed from

Run Menu
Choose Active Run Configuration
Manage Run Configurations 
Edit Default

Add the line to the end of the Java Options.

 Also if  you see a large work:WorkContext section in the SOAP header, you can remove that by adding this line to the startup.
-Dweblogic.wsee.workarea.skipWorkAreaHeader=true
 






Thursday, October 31, 2013

How to make popup that can be dragged past edge of browser window.

I had a large dialog that I wanted to be able to move out of the way so that I could see what was behind it without closing it.   Normally any part of the dialog cannot be dragged off the edge of the browser window.   Configuring this turned out to be pretty simple.  Just add an inline style for the margin and give negative numbers to the sides you want to drag off the edge of the screen.  In the example below I don't want the top to be draggable past the top edge (because the top bar wouldn't be visible to drag it back down)  The left and right I set to -200 so that I could drag it 200 pixels to the left or right of the windows edge and 300 pixels past the bottom of the window.   This allows me to move it mostly out of the way without having to close it.



   <af:popup id="ppcal" clientComponent="true">
    
<af:dialog id="d1" title="Calculator"
               modal="false" type="ok"
               styleClass="AFStretchWidth"
               inlineStyle="width:450px; margin:0px -200px -300px; padding:-200.0px>

Tuesday, September 10, 2013

Displaying external content in a IFRAME within a popup dialog

I recently had a requirement to display some non ADF content inside a popup dialog.   There are a number of blogs on how to do this (see this one)  so I won't rehash them here.  I did however run into one problem in that when you drag the dialog around on the page and the mouse cursor slips into the frame containing the external content, the mouse events get captured by the frame and the dialog stops dragging.  Then if you release the mouse button and move outside of the frame, the dialog is now stuck to the end of your mouse cursor because the page lost the mouse up event and still thinks the mouse button is pressed.   To fix this I had to write a javascript mouse event handler and attach it to the frame to propagate mouse events in the frame to the parent page.

I found the basis for my solution here.  mousemove-when-over-an-iframe

I modified this slightly in that my particular page didn't need to save the original onMouseMove and onMouseUp handlers.


To wire this up add a client listener to the popup
  <af:clientListener type="popupOpened"     method="popupOpened"/>
 


 
// This method attaches the frame with id of "fid" to the mouse event handler

 function popupOpened(e){
      var frame = document.getElementById( e.getSource().findComponent("fid").getClientId()+'::f');
      bubbleIframeMouseMove(frame);
  }

// iframe causes the page to lose the mouse events when dragging the dialog
// this fixes it by propagating mouse events in the iframe to the parent page

function bubbleIframeMouseMove(iframe){
 
     iframe.contentWindow.onmousemove = propagateToParent;
     iframe.contentWindow.onmouseup = propagateToParent;
      
      function propagateToParent(e){
        // Create a new event for the this window
        var evt = document.createEvent("MouseEvents");
        // We'll need this to offset the mouse move appropriately
        var boundingClientRect = iframe.getBoundingClientRect();
        // Initialize the event, copying exiting event values
        // for the most part
        evt.initMouseEvent(
            e.type,
            true, // bubbles
            false, // not cancelable
            window,
            e.detail,
            e.screenX,
            e.screenY,
            e.clientX + boundingClientRect.left,
            e.clientY + boundingClientRect.top,
            e.ctrlKey,
            e.altKey,
            e.shiftKey,
            e.metaKey,
            e.button,
            null // no related element
        );

        // Dispatch the mousemove event on the iframe element
        iframe.dispatchEvent(evt);
    };
}




Friday, July 19, 2013

Using XML data in ADF View Object

I recently was tasked with creating a generic preferences table that can be used to story any type of web site preference.   This needed to be generic enough so that new types of preferences can be added without requiring a data model change.  Using XML in the value column was the perfect solution for this.

This is how the table is defined


  CREATE TABLE "USER_PREFERENCE"
   (
     "USER_PREFERENCE_ID" NUMBER(38,0) NOT NULL ENABLE,
    "USER_ID" NUMBER(38,0) NOT NULL ENABLE,
    "PREFERENCE" VARCHAR2(40 BYTE) NOT NULL ENABLE,
    "VALUE" VARCHAR2(4000 BYTE) NOT NULL ENABLE
}

The first type of preference I needed to add was a URL link. I defined this in XML


<url >
 
<label>Yahoo </label>
  <location>http://yahoo.com/location>
  <open>defaultBrowser</open>
  <sortorder>1</sortorder>
</url>

I insert this xml into the value column and "CUSTOMLINK" into the preference column along  with the USER_ID and primary key USER_PREFERENCE_ID sequence.


Here is the query I used to retrieve my custom url links.  Notice that I'm not actually returning any column from the table.   All of the columns returned are coming from the xml nodes in the value column.  This query uses a feature of Oracle available since 10.2 (XMLTABLE).  You can think of it as a table within a column that the row is being joined to.  "url "is the alias for this table containing all of the xml elements as table columns.



    SELECT
           url.label,
           url.location,
           url.open,
           url.sortorder
    FROM USER_PREFERENCE ,
    --  XMLTable defines a join with contents of value column treating it like a table
              XMLTable('url'
                       PASSING XMLTYPE(USER_PREFERENCE.value) 
                       COLUMNS
                       label  VARCHAR2(40) PATH 'label',
                       location  VARCHAR2(1025) PATH 'location',
                       open VARCHAR2(40) PATH 'open',
                       sortorder VARCHAR2(4) PATH 'sortorder',
                       permission VARCHAR2(40) PATH 'permission'
                       ) as url
   
    WHERE PREFERENCE = 'CUSTOMLINK'
    AND  USER_ID = :UserIdBind
    order by sortorder




I used a read only view object for this.   I could have created a entity based view object and use expert mode to insert similar sql.   For storing and committing data in the value column I would need to add custom code to the setters for the 4 xml based columns and there construct the xml containing the values from all of these columns and then set that xml into the value column.

This gives me great flexibility for adding additional columns to my query without making any changes to the data model.  Querying XML will probably not perform well if you have a huge result sets but  is a great solution for queries that return a small number of rows as this one does.

Thursday, May 2, 2013

Hot deploy in R1

ADF Release 1 of jdeveloper has limited support for hot deploying java code.  When running the integrated Weblogic server in debug mode, JSPX/JSFF files will hot deploy as soon as you save and reload the page.    You are also allowed to make changes to java methods as long as you don't change the structure of the class.  This means you can't add or remove methods, change method signatures or add or remove class properties or static members but you can change the code inside a method and instantly deploy the change while the server is running.   To do this hit CTRL+SHFT+F9 key combination.   This will compile and deploy the single java class that has been changed.   If it is successful you will see the message
"Redefined all recompiled classes that are loaded in the debuggee process.” 

 For a long time I had been using CTRL+F9 which performs a full build and hot deploy of the application.   Our application is very large and this would take over a minute to complete but  CTRL+SHFT+F9  will hot deploy a single file in about 1 second.  I don't know why this key combination is not front and center on the build menu (since CTRL+F9 is).   It is listed in the context menu that displays when you right click on a Page. (see image)  but I never noticed that the "Make" on the context menu was different than the "Make" on the Build menu so didn't pay attention to the hot keys listed.    It is a huge time saver but the only way to learn that this is a "single file" make command is to browse the Tools Menu (Preferences/Shortcut Keys) to find it.   CTRL+SHFT+F9  is now my most favorite keystroke.

Release 2 has much betters support for hot deploy.  I'm not using release 2 yet but here is blog that describes What works and what doesn't.