Internal ADF API Survey
In conjunction with the ADF Methodology Group I'm conducting a survey into the use of internal APIs within within ADF applications (for example
oracle.adfinternal.view.faces.model.binding.FacesCtrlHierBinding).
We are going to be introducing some audit rules into the product to help catch these, however, I do need help in tracking down the wild and wacky usages to which these internal APIs have been put, that way we can work out:
- If we need to create a public API to fulfil this programming requirement
- Suggestions for re-writing the code so that it uses existing public APIs
- Examples of just plain dangerous stuff to do.
If you are willing to help please let me know at devtools-feedback_ww at oracle.com . I'm aiming to share the results at either a session or an unconference session at OOW, plus of course we'll push more smarts into JDeveloper.
I have published a list of the "bad" packages in the following document, http://groups.google.com/group/adf-methodology/web/adf-internal-api-usage.
Please join in and help!
Template Manipulation at Runtime
I've just posed a new sample up onto samplecode.oracle.com which shows how a page that consumes a template can interact with components in that template to do things like collapse splitters and so forth. Importantly this is achieved without polluting the template with knowledge of the consuming pages. The sample shows how to perform the interaction both for full page and more importantly partial page refreshes.
The sample code project includes a full JDeveloper workspace and has no external dependencies. 11g is of course required.
If you have a little sample like this why not publish it to samplecode as well? - use the JDeveloper and ADF category.
Dynamic Default Values for Entity Attributes
Working on my of our internal systems this afternoon I hit one of those requirements that you push to the end of the to-do list and never quite get around to doing until they turn around and bite you.
In my case it was a matter of default values. This particular system needs to know about which Quarter something happens in, and I'd sort of temporarily hard coded FY09Q1 into the system until I go around to doing it properly. You guessed it I then totally forgot to do that and deployed the system live with the hardcoded value.
Sure enough everything was fine for a month or so until FY09Q1 was no longer the right answer anymore and weird stuff started to happen.
Ok we've all done it, fix it and move on.
So let's consider the use case...
Here the quarters are actually being read from a table which maps the start and end of each of Oracle's quarters for the next 10 years or so. Therefore the requirement here is really to set up the default value for the Quarter attribute to equal the Quarter name from the correct row in the Quarters table that matches today's date. Essentially a dynamic default value being read from a table based on some other calculation or factor, rather than being just a simple hardcoded value which we usually use in the default.
The question is can you do this in a declarative way? The answer is yes, thanks to the power of Groovy!
- The first step is to create a new View Object that gives me access to the row I needed that contains the correct Quarter for today's date. Simple enough to do with a Where startdate < = :TODAY and endate >= :TODAY, where :TODAY is a bind variable which has a default value which is set to the Groovy expression
adf.currentdate
> - VO does not need to be exposed through the AM as I only use it internally within the model. To get to it using Groovy I needed to open up the entity that contains the Quarter attribute that I wanted to default and create a View Accessor to this VO. This allows me to programmatically walk from the entity to the rows returned by the VO and if I needed to, pass values to the bind variable used by that VO. In this case, the default value of today's date will be just fine though. I called the accessor
DefaultQuarterAccessor -
Finally onto the default value itself. Change the value type to Expression and set the expression to
DefaultQuarterAccessor.first().Quarter, where first() indicates that I want the first (and only) row in the Accessor rowset and from that I want the Quarter Attribute from within that row.
This same technique can be used to do all sorts of useful dynamic things with validators, error messages and default values without having to code up Impl files in Java. For example I use the same approach to look up friendly values for error messages. So rather than a basic message like "salary is too high for department 40" we can walk the related rows and come up with a much more meaningful messages such as: "Salary in Sales Administration is capped at 20,000" where I'm looking up both the proper name of the department and it's actual maximum value using Groovy expressions.
One note of caution though, it's easy to get confused between Groovy usage notation like this and the expression language you use in your view pages. They are different.
Note: This code refers to the production release of JDeveloper 11.1.1.0 (and patches) AKA "Boxer".
Using ORDIMAGE in R11
At the moment there seem to be a few glitches in ORDIMAGE support with JDeveloper 11 and some traffic on the forum. Just as a hint here's a snippet using af:media to display a image stored in an OrdImage attribute. note that I've had to expand the servlet reference out (that's the W/A for the current bug. Also if you've used this in 10.1.3, note that the expression for the Mime type is slightly different (excuse the wrapping in the source value):
<af:media source="/ordDeliverMedia?appModID=TuhraServiceDataControl&
viewObjectName=TuhraService.AllImages1&
contentCol=Image&
rowKey=#{bindings.AllImages1Iterator.currentRowKeyString}"
player="windows"
autostart="true"
contentType="#{bindings.Image.inputValue.mimeType}"/>
inputText to Uppercase - let me count the ways
So, it's funny how the simplest of things can lead you off on an interesting road of discovery, particularly at 5:25pm when you just wanted to add that little feature into a screen before heading home. So last night I was adding some validation onto a form. I had a email field and decided to use the regular expression validator provided by ADF BC to manage the validation. The expression for validating email format (you know someone@somewhere.com/org/net) just happens to be one of the supplied expressions in the tool - Great Zero effort! Woops - slight problem my screen is failing, ah on closer inspection, this pre-canned expression expects the email address to contain only uppercase characters - OK I have three choices here.
- Change the expression to support mixed case - Yes trivial to do but....
- Log a bug for someone else to do the above and wait for the fix - naaa
- Convert the contents of the field to uppercase - That's the one!
So after a little work in Google I was surprised to see that we could actually achieve this in no less than 5 ways with the combination of ADF Faces Rich Client (R11) and ADF Business Components. Here they are:
- If you're using JHeadstart then that has a property on the defintion to enforce this kind of thing, and then under the covers they use JavaScript (see option 4) to do it
- In ADF BC using a bit of code in the setter for the field to .toUppercase() the string as it comes in - you could see how this could be made generic using a custom property in the UI Hints for that attribute and a generic Superclass. So that would be a very workable solution and would apply with any client. However, no instant feedback on the client, the uppercase version would only appear after the field had been submitted up to the model
- Using Styles - Ah sounds simple enough - sure enough setting
contentStyle="text-transform:uppercase;"as an attribute on the <af:inputText> works just fine in terms of showing the data in upper, but it's eye candy only, the value of the field is still in mixed case when saved to the model - no good for this purpose then - Using a JavaScript call and a ClientListener. This approach is detailed by Lucas Jellema over on the AMIS Blog - good stuff as usual, but it seems a messy approach to me from the re-use perspective
- Use a Custom Converter. This is something that Matthias Wessendorf blogged about in relation to building converters which have client side capabilities. This turned out to be just the ticket - You get instant conversion to uppercase as you tab out of the field, plus the way to enable uppercase on a field is simply to add a converter tag where needed with no need for script in the page:
<f:converter converterId="StringAsUppercase"/>
There you go, I need to clean up the converter code and make it more useful to turn it into a more generic case converter supporting initcap and lower case as well, at that point I'll publish the code jar (and make the source available) so it can just be dropped into your projects.
:: Next Page >>