Wednesday, February 3, 2010

Error Handling and the Binding layer

Well...

Jan has beat on the subject of error handling, and I believe Frank Nimphius and Steve Muench have both thrown in their two cents. But I still had some nagging questions, so I pressed him; he did amazing things, and I think I responded pretty well. It was not a bad day all in all.

Check this JDev thread out if you have been wondering about using operation bindings versus calling the app module methods directly.

Op binding/error handling thread on Oracle JDev Technet forum

Wednesday, January 13, 2010

Tomahawk Popup loss of focus

A while back I realized that when using the tomahawk popup component (t:popup) in my ADF faces page that I was having a problem: whenever I had a field (like an af:inputText) inside the popped-up region along with something else, I would get some behaviour I did not want: as soon as my cursor left the field (but stayed within the popup region) focus would leave the input field. This made it awkward when I wanted to have a search field in the popup. But I found a simple solution: in the inputText's onblur property, put the following code: try{ this.focus(); } catch(e){}

What this does is keep it there rather than lose focus. I would really like to know what code is making this field lose focus. I did some searching, but it was very slow going for me, and I needed a quick fix.

Friday, January 8, 2010

JSFBlueprint auto-suggest field enhancements

What follows is an listing of the javascript that goes with the auto-suggest field you can find in JSF Blueprints.

The original brought up a basic list of suggestions which could be mouse selected. I added code to allow you to arrow down and up directly if you did not want to reach for your mouse. Also I added code (per a suggestion by Frank Nimphius, Oracle Corporation), which would reduce the number of ajax calls...very important for many use-cases of this component.

gReq = "";
var req;
var requests;
var target;

function getElementX(element){
var targetLeft = 0;
while (element) {
if (element.offsetParent) {
targetLeft += element.offsetLeft;
} else if (element.x) {
targetLeft += element.x;
}
element = element.offsetParent;
}
return targetLeft;
}


function getElementY(element){
var targetTop = 0;
while (element) {
if (element.offsetParent) {
targetTop += element.offsetTop;
} else if (element.y) {
targetTop += element.y;
}
element = element.offsetParent;
}
return targetTop;
}

function getWidth(element){
if (element.clientWidth && element.offsetWidth && element.clientWidth < element.offsetWidth) {
return element.clientWidth; /* some mozillas (like 1.4.1) return bogus clientWidth so ensure it's in range */
} else if (element.offsetWidth) {
return element.offsetWidth;
} else if (element.width) {
return element.width;
} else {
return 0;
}
}

function initRequest(url) {
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
} else if (window.ActiveXObject) {
req = new ActiveXObject("Microsoft.XMLHTTP");
}

// ********** INSERTED CODE ***********************
// The general idea here is that, the end user is only interested in the
// latest request. As of right now (9/20/2009), I have not seen anything
// to indicate otherwise. Assuming this is true, I am hoping that
// we never have a corresponding backlog of request on the database end.
if (typeof(gReq) != 'undefined' && typeof(gReq.readyState) != 'undefined'
&& typeof(req) != 'undefined' && typeof(req.readyState) != 'undefined') {
// alert("gReq.readyState is " + gReq.readyState + ", and req.readyState is " +
// req.readyState);
gReq.abort();
}
gReq = req;
//1/1/2010, mfons, The above was a brilliant idea, but it did nothing I can discern: still
// querying every keystroke. So now I
// will try waiting .5 seconds before registering the keystroke...if
// they are still typing I will keep waiting until I get something
// that has not changed...then query. See doCompletionDelayed()
// function below.
// ************ END OF INSERTED CODE **************
}

/**
* 12/31/09, mfons - originally doCompletion() called the ajax for each keyclick,
* but really we only want to only call the db if the user has not typed anything new
* in a while. Credit for this idea goes to Frank Nimphius, Oracle Corp.,
* who responded to my query on the technet.oracle.com jdeveloper forum.
* However, I figured the coding out myself.
**/
function doCompletionDelayed(targetName, menuName, method, onchoose, ondisplay, oldTargetValue) {
var target = document.getElementById(targetName);
if (target.value == oldTargetValue) {
var menu = document.getElementById(menuName);
menu.style.left = getElementX(target) + "px";
menu.style.top = getElementY(target) + target.offsetHeight + 2 + "px";
var width = getWidth(target);
if (width > 0) {
menu.style.width = width + "px";
}
var url = "faces/ajax-autocomplete?method=" + escape(method) + "&prefix=" + escape(target.value);
initRequest(url);

if (!requests) {
requests = new Object();
}
requests.menu = menu;
requests.onchoose = onchoose;
requests.ondisplay = ondisplay;
requests.targetName = targetName;

req.onreadystatechange = processRequest;
req.open("GET", url, true);
req.send(null);
}
}

function doCompletion(ev, targetName, menuName, method, onchoose, ondisplay) {
var menu = document.getElementById(menuName);
var evt = (typeof(ev.keyCode) == 'undefined') ? window.event : ev; //IE reports window.event not arg
//alert('ev.keyCode is '+ev.keyCode+' and window.event.keyCode is '+ window.event.keyCode);
/*
Try #1: I am trying to allow people to select items with the arrow keys as
well. Not just on a click of the anchor.
*/
/****************ADDED CODE BEGIN***********/
if (typeof(evt) != 'undefined') { // in firefox, focus event may leave evt undefined.
if (evt.keyCode == 38 /*up*/ || evt.keyCode == 40 /*down*/) {
try {
var childAnchors = menu.getElementsByTagName("a");
var targetAnchor = childAnchors[0];
// Look for highlighted item. If found
for (var i = 0; i < childAnchors.length; i++) {
if (childAnchors[i].className == "selectedPopupItem") {
if (evt.keyCode == 38 && i > 0) {
childAnchors[i].className = "popupItem";
targetAnchor = childAnchors[i - 1];
}
else if (evt.keyCode == 40 && i < childAnchors.length - 1) {
childAnchors[i].className = "popupItem";
targetAnchor = childAnchors[i + 1];
}
else {
targetAnchor = childAnchors[i];
}
break;
}
}
targetAnchor.className = "selectedPopupItem";
} catch (e) { alert("error");}
return;
}
else if (evt.keyCode == 13 /*Enter*/){
try{
var childAnchors = menu.getElementsByTagName("a");
for (var i = 0; i < childAnchors.length; i++) {
if (childAnchors[i].className == "selectedPopupItem") {
try{
childAnchors[i].click(); // It appears that firefox has no "click()" defined for anchors??
} catch(e) {
childAnchors[i].onclick();
}
stopCompletion(menuName);
return;
}
}
} catch(e) {}
}
else if (evt.keyCode == 27 /* Escape */ ) {
stopCompletion(menuName);
return;
}
}
/****************more added code...***************/
// 1/1/2010, mfons, added the following call in order to avoid launching an
// ajax-request (i.e., doing a database query) for each keystroke.
// This wait make sure that the target value does not change for some
// time period (500ms at the moment) before actually doing the query.
var target = document.getElementById(targetName);
//alert(target.value);
setTimeout("doCompletionDelayed('" + targetName + "', " +
"'" + menuName + "', '" + method + "', '" + onchoose + "', " +
"'" + ondisplay + "', '" + target.value + "')", 500);
/****************ADDED CODE END***************/

}

function chooseItem(targetName, item) {
if (!requests.onchoose || requests.onchoose == "null") {
var target = document.getElementById(targetName);
target.value = item;
} else {
requests.onchoose(item);
}
}

function stopCompletion(menuName) {
var menu = document.getElementById(menuName);
if (menu != null) {
clearItems(menu);
menu.style.visibility = "hidden";
}
}

/* Stop completion shortly.
This is necessary because I want to stop completion from the blur
(focus loss event) of the completion text field, but that will also
happen, right BEFORE a link click in the completion dialog is processed.
If this is done synchronously, the link is deleted before it is processed
by stop completion. Therefore, I use the delayed variety which schedules
stop completion instead such that the link is processed first.
*/
function stopCompletionDelayed(menuName) {
/* Would like to shorten timeout but this seems to trip up Safari */
setTimeout("stopCompletion('" + menuName + "')", 400);
}

function processRequest() {
if (req.readyState == 4) {
if (req.status == 200) {
parseMessages(requests.menu);
} else if (req.status == 204){
clearItems(requests.menu);
}
}
}

function parseMessages(menu) {
clearItems(menu);
menu.style.visibility = "visible";
//alert(req.responseText);
var lItemsRE = new RegExp("<item>(.*?)</item>", "g");
var lItems = req.responseText.match(lItemsRE);
for (var i = 0; i < lItems.length; i++) {
//alert("item "+ i+ " is " + lItems[i].replace(lItemsRE, "$1"));
appendItem(menu, lItems[i].replace(lItemsRE, "$1"));
}

// var items = req.responseXML.getElementsByTagName("items")[0];
// for (loop = 0; loop < items.childNodes.length; loop++) {
//
// var item = items.childNodes[loop];
//alert('item is '+ item); // why is item null now? xml is coming back OK, it appears.
// appendItem(menu, item.childNodes[0].nodeValue);
// }
}

function clearItems(menu) {
if (menu) {
for (loop = menu.childNodes.length -1; loop >= 0 ; loop--) {
menu.removeChild(menu.childNodes[loop]);
}
}
}

function appendItem(menu, name) {
var item = document.createElement("div");
menu.appendChild(item);
var linkElement = document.createElement("a");
linkElement.className = "popupItem";
linkElement.href = "#";
linkElement.onclick = function() {
chooseItem(requests.targetName, name);
stopCompletion();
return false;
}
var displayName = name;
if (requests.ondisplay && requests.ondisplay != "null") {
displayName = requests.ondisplay(name);
}
linkElement.appendChild(document.createTextNode(displayName));
item.appendChild(linkElement);
}

This code has been tested against IE 7 and 8 and also the latest version of FF.

Please let me know if this is helpful.


Thursday, December 17, 2009

default database values

If you get an error that indicates that ADF/BC (the version out of JDev 10g) has compared the value it originally retrieved from the database to the value now in the database, and finds that they have changed; please, bear in mind that a database default value will do this on new records also!

So for example: you are now editing a record that you just created, suddenly you get this jbo error on a postback. The ADF Developer's Guide... says that in the case of columns altered by triggers associated with an insert, that you will need to check the "update after insert" checkbox in the entity for that attribute. The documentation does not specifically mention database table column default values, but these will have the same effect.

Once you realize this is what is happening you need only check these checkboxes to fix this.

Monday, December 14, 2009

Just updated my blog profile

I just updated my blog profile to have a valid email address. I forgot that the one in there was not valid, since I am not job-hunting at the moment.

Sorry if it has caused me to give the appearance of ignoring anybody's questions.

Friday, December 11, 2009

adding 1 or more blank rows to your af:table for data entry

Boy am I embarressed. I read the whole 1160 ADF 10g developers guide for 4GL developers from Oracle, have professional experience with building ADF, and claim to be becoming an expert in all this stuff.

But I had failed in getting this simple thing done before successfully: have more than one rows availble in an af:table to allow data entry. I kept running into trouble with validation on the second row. I knew how to do what I wanted in Oracle*forms, just not ADF/BC/ADF Faces!

To my credit though I am sure I must have inquired of the JDev Forum at least once before on this subject. Literally I had come up with an "alternative" solution on a prior contract, but it was WRONG!

The correct solution is very simple: like it says in the manual: whenever you do a VO.create(); follow that by taking the resulting row and saying resultingRow.setNewRowState(Row.STATUS_INITIALIZED);

Then you can VO.insertRow(resultingRow), or do whatever you want.

The odd part of this is, I guess, if you have overridden your initDefaults in the VO's entity, using the entity's setters may changed the status of this VO row from STATUS_INITIALIZED to STATUS_NEW. I kinda figured that if you have setter method calls (e.g., this.set...(blah))in that method that you would not "dirty" your row or entity. I guess that is not right.

At any rate: that's how it is supposed to be done.

So embarressed.

Sunday, December 6, 2009

The proper tool for the job

My grandpa used to say, "The proper tool for the proper job!" I knew what he meant, and it seemed to apply to what I was experiencing yesterday.

I am using some Dojo widgits to get ADF Faces (JDev 10g/Trinidad) some extra functionality. It integrates pretty well. The way I am doing the integration each widgit I use requires a hidden field in the background. But I think there is a caveat. There is something that does not work right in some JavaScript I am using so that the integration code does not work right if there are only af:inputHidden's on the screen, and no actual input fields...just the dojo widgits providing that function. In other words there still needs to be at least one inputText for the dojo integration to work right. No problem, I just made my first dojo integrate with an af:inputText with a style of "display:none;". Works fine normally.

Well yesterday and the day before it was NOT working fine. I was imagining a SIMPLE use-case whereby I had several Dojo rich text editors on the same page. I thought: I will just bind the value of my hidden af:inputText to a backing bean getter/setter that just pulls the data it needs from the database when it needs it and caches it when possible on a session bean HashMap to avoid needless trips to the database.

I typed out some code how I imagined it would work...that was 3 working days ago!

Since then, trying to make af:inputText work, knowing what I know, I think I have encountered every caveat know to man related to using af:inputText and not binding to the database with ADF bindings. For one thing my setter would not fire! The processing would go through the motions like it WANTED to fire (i.e., it was firing "isReadOnly()" in the PropertyResolver when it was trying to evaluate my value binding. The only reason I know of to fire isReadOnly() is to test for fitness for doing a set, and every call it made returned "false", so I kept waiting for the setter to fire. It did not!

So I started thinking of alternatives. I thought: maybe I can use a valueChangeListener. It turned out that either the presense of the value binding or perhaps references to the binding layer (not for a value but just for VO attribute metadata purposes, like getting a value for required property) was diabling my use of the valueChangeListener, so I had a hard time even getting that to fire. When I got it to fire, it was without the value binding. So I switched to bind to an input component in my backing bean.

The nice thing about my original plan is that the getting and setting happens at all the right time, and since my af:inputText was inside an af:iterator loop, I need access to the requestScope "var" variable generated each loop. Using the getters and setters I had the perfect alternatives. But since that was not working out so well, I now was trying to figure out how to use this component I was bound to that is going through the ringer anyway to populate it. I was imagining that I would ultimately need a custom component that would give me the control I needed to be able to fire things at the right time so that I could hit the database for the values I wanted in time to use them as Dojo was loading, and also be able to save them back if someone updated the Dojo widgit.

Anyway, I was really not looking forward to going down that road, so after doing a few more experiments, I finally tried using h:inputText and going back to my original plan.

IT WORKED BEAUTIFULLY.

I was grateful; I did not have to slit my wrists after all! It worked so beautifully that I literally just connected up that code I created initially for the getter and setter, and everything worked according to plan, just like I had imagined.

I think the lesson I learned was, if you are going through ADF binding layer, there is probably no better solution than using af:inputText, and the like. And if not...if you have a simple JSF usecase that does not seem to fit the ADF Binding scenario, maybe you are (or at least, I am) better off using good old JSF components for simplicity and predictability sake.

One thing in my career I am trying to obtain is some depth and breadth to my knowledge. I think yesterday helped me obtain that just a bit more.

Wednesday, November 18, 2009

Job posting

We are looking for a new addition to our team out in Fairfax, VA, to help out with ADF work. We are trying mightily to convert an old web-pl/sql toolkit/portal system to ADF Faces. Right now our target for conversion is the ADF Faces (JSF 1.1) which comes with JDeveloper 10.1.3.x. We are deploying to OC4J/iAS 10.1.3.4

Ultimately we would like to get to ADF Faces Rich Client (JSF 1.2) version, but that is contingent on resolving some licensing issues.

So, if you know ADF/JSF/J2EE/Java EE/Java/JSP and are looking for work in the Fairfax, VA area please contact me at michael_fons at yahoo dot com.

Limited telecommuting might be possible, but more than likely you would be commuting (like me) or moving to this area on some basis.

U.S. Citizenship required.

There is also some BI reporting work to do as well, so if you know BI or Pentaho that would be a plus.

Wednesday, October 28, 2009

What to study next...

So many technologies I want to study further: JSF 2.0, GWT, ADF Faces Rich Client component library, deployment of Java EE apps to phones and pda's, Scala, Wicket, Tapestry, Facelets, et cetera ad nausium.

...so little time. I wish we could learn like they did on the matrix :-)

Monday, October 19, 2009

Oracle Open World Aftermath

I definitely owe a blog entry at this point!

Just came back from Open World, and boy: is my brain tired!! (ba-dum-CRASH)

Anyway. I had a request that I make some specific entries on my blog from someone in the audience that came up to visit after my presentation.

He wanted to know how to do two things that I mentioned:
1. how to use the phase listener to get resource files in a custom component, and
2. how to use a renderer to divide up functionality.

These are somewhat meaty topics but I will try to be concise.

I. PhaseListener "magic"
If you have Schalk and Burns's
JavaServer Faces: the complete reference then just (re)read Chapter 11.

If you do not...here comes the paraphrase. The general idea is that you need to add a phase listener to your application in the normal manner: by adding a few lines to your faces-config.xml file. Note that if you have a custom component in JSF, then your jar has in it in its META-INF directory a faces-config.xml file, where you can reference this phase listener. JSF guarentees that this will be read first before the one in your application, I believe, provided this jar is in your WEB-INF/lib directory of your application.

Anyway you take your added phase listener, and make it listen for the RESTORE_VIEW phase by overriding the getPhaseId() method, and returning PhaseId.RESTORE_VIEW constant.

So let us say for example that your component has a need to have javascript code, that you want to keep in a sepparate file...because there is too much code to render on the html page itself (or you are a "tidy" individual). So your component will encode/render a javascript "include" (i.e.,<script src="somename" type="text/JavaScript"></script>

). So you encode this line onto the page. Then when this page is "interpreted", and issued as a request, your phaseListener can be ready. Your phaseListener has knowledge that at some point after the RESTORE_VIEW phase is complete, the FacesContext.getViewRoot().getViewId() will contain "somename". At that point, the phase listener can use getClass().getResource("somename's filename"), to get a url. This url can be connected-to and read from, an inputStream, then an inputStreamReader, then a BufferedReader. Meanwhile you open the outputWriter from the response on the FacesContext's ExternalContext, create an outputStreamWriter, then do a while loop reading lines from the buffered reader and writing lines to the outputStreamWriter, until there are no more lines to write. By the way the FacesContext is available from the PhaseEvent that your receive as a parameter. And with this FacesContext, you finish with a call to its responseComplete() method.

II. Renderer.

This is easier. If you have a custom component that has no renderer you can move your encode*() methods and decode() method to the a custom Renderer class. The main changes you will need to make are because you now no longer say "this" when you need to reference your component. In your new renderer class (which BTW extends "Renderer") the encode*() and decode methods receive your component as a parameter, so you much change your "this" references to use this parameter.

You must also augment the faces-config.xml, to have a element with a sub-element which shows the family, type, and class of your renderer. Type can be anything, but whatever it is must now also be returned by getRendererType() overridden method in your tag handler class.

Also you will need to guard against receiving a null FacesContext or a null Component value for your parameters. This is in the spec so don't shoot me; I'm just the messenger.

Sir, I hope, whoever you said you were, that this blog entry, approaches meeting your custom-component writing needs or curiosities, or whatever drove your question. I bid you: good day!

I bid the same for the rest of you as well.