Categories

Duncan Mills

Syndicate this blog

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!

  1. 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
  2. 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
  3. 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".

Propagating ADF Authentication Identity into the ADF BC layer in ADF 11g

For the security chapter of the Fusion development book I’ve been polishing off the security section with the basic use case of how to take the identity that the user has authenticated to the container with and then using that to interrogate the database to get useful information about the user (for example their real name). This is a pretty common requirement and we did something like it in the last book to set up PL/SQL context for a session.

There are several ways to approach this problem; the most linear thought-line being:
OK I have the Java EE container username in the HTTP session, so I can call a method on the AM that passes that information in, queries the User table and then returns the name information for storage in the session.
Well that’s fine but it has several flaws:

  • We don’t really need to pass the principal name to the ADF BC layer it’s already available via the SessionImpl.getUserPrincipalName()
  • Why pass the information back into a session state variable at all? We can just define a single row VO which points to the user state information and bind the client to that to get to the information when needed. The advantage of this is that I won’t have to do additional work in the session when / if the user logs out and changes identity.

So I had a clear idea of how I wanted to do this but the problem remains - how do I refresh the UserInfo VO when the login happens - so that it does not contain stale information?

My first stab at this ran into a brick wall, I defined a custom method on the UserInfo VO which grabbed the User Principal, assigned that to a bind variable and re-executed the query on the VO.

I could see this was working just fine, when called from my JSF Login action, however, big problem, my UI fields bound to the UserInfo VO attributes kept coming up blank.

Nothing to do here but switch on a bit of logging to see what is going on:

-Djbo.debugoutput=console

Then I saw the problem - the technique that I was using to handle the login was essentially starting in a new ADF BC session (because I was doing a re-direct as a lazy way to prevent me from having to set up a bunch of screen refresh PPR which is fine in the use case I have). So looking at the log I could see the bind variable being set up correctly, the query on UserInfo executed, then the whole thing repeated this time with the default value of the bind variable rather than the User Principal name value I’d set up. Bummer.

Step back, think about the problem….

Ok, so the VO is being refreshed with the default value of the Bind Variable - what if I could set the default value of the bind to the User Principal name? If did that I’d never even have to call a VO method to set the Bind Value, it would all be automatic.

Of course in 11g we have Groovy expressions which can be used in a whole bunch of places, validation of course and setting default values, both for EO attributes and for Bind Variables - Ahah!

In the end it turned out to be too chuffing easy, all I had to do was set the default value of the bind variable on UserInfo to an expression rather than a literal value and then use the expression viewObject.DBTransaction.session.userPrincipalName. That’s it.

With this in place there was no need to make a call from the client login code at all, it was all just automatic. I ended up with a simple VO with no impl files and all the refreshing is handled implicitly, even as you switch users. It all goes to prove that the Groovy expression capability in 11g is one of the most useful new features that we put in for the BC layer.

Easy when you know how….

A Rough Guide to

Installing and Setting up WebLogic 10.3 Production for Running ADF Applications

The process of setting up and running WebLogic Server to handle ADF 11g applications appears to be causing some confusion so this blog entry documents the basic steps required to setup and configure WebLogic (both and Admin server and a Managed server) for ADF Applications.
This is not the official documentation, but rather it's the basic steps I followed this evening to set up a configuration from scratch. So it's rough but it does work.

  1. As prerequisites I’m using WebLogic 10.3 downloaded from OTN and the latest version of JDeveloper (11.1.1.0.1 build 5188)
  2. Install WebLogic into a new BEA home. I’m doing this on M/S Windows and my home directory will be C:\builds\WLS10_3
  3. While installing you can go for a custom install and deselect the following which you won’t need:
    • Workshop
    • Web 2.0 http pub-sub server
    • Weblogic Web Server Plugins
    • UDDI and Xquery Support
    • Server Examples
  4. Select the JVM that you want to use. Here I’m just using JRockit
  5. Don’t bother to install the node manager service. If you use it to start a managed server then ADF apps will not run because the classpath is somehow not set up correctly - so you need to start the admin server and managed servers from the command line.
  6. Complete the install sticking to the defaults, but uncheck the Quickstart option on the last screen
  7. Now run the JDeveloper install on the target machine
  8. Choose existing middleware home in the home type and select the home that you just created for the server install (e.g. C:\builds\WLS10_3 in my case)
  9. On the product selection screen make sure that both JDeveloper Studio and ADF Runtime are installed.
  10. Run the install.
  11. Now run the Configuration Wizard from the start menu
  12. Create a new WebLogic domain
  13. Choose the first option on the next screen to pre-configure the domain with both WebLogic Server and ADF
  14. Create the admin user as weblogic / weblogic (or whatever). As we’re trying to emulate a production instance here choose production mode and JRockit as the VM
  15. On the next screen you can choose to customize things like the ports - so choose yes there so we can reassign out server to use ports 80 and 81
  16. Just choose next on the RDBMS security store page
  17. On the configure the Admin Server page set the port to 81
  18. On the next page Add one managed server called ManagedServer and set the port to 80
  19. No need for clustering in this simple case so next through the cluster page
  20. Create a machine on the next screen. If you are using a machine with a known DNS name and fixed IP address use that. or just create a arbitrary name such as LocalMachine if this is all for local testing
  21. Next through to the summary screen
  22. In the Create WebLogic Domain screen change the name of the domain to ADFDomain and press create.
  23. Finish the wizard.
  24. Now from the command line start the AdminServer with the script (in my case): C:\builds\WLS10_3\user_projects\domains\ADFDomain\bin\startWeblogic.cmd
  25. You need to specify the username and password if you defined the Domain in production mode.
  26. Once the AdminServer is started run the console at http://localhost:81/console
  27. Log in.
  28. In the console, click deployments in the Domain Structure tree You should see the following Deployments:
    • adf.oracle.domain(1.0,11.1.1.0.0)
    • jsf(1.2,1.2.7.1)
    • jstl(1.2,1.2.0.1)
  29. Press Lock and edit in the change center box at the top left of the screen.
  30. Drill down through each of the Deployments and select the Targets tab. For each one check the checkbox for both the ManagedServer and the AdminServer
  31. Save each deployment change as you make it
  32. Finally press the Activate Changes button when they are all done.
  33. Now to start the ManagedServer - use the command line for this: “C:\builds\WLS10_3\user_projects\domains\ADFDomain\bin\startManagedWeblogic.cmd ManagedServer http://localhost:80”
  34. Now we can run ADF applications on both servers
  35. Now from JDeveloper you will be able to create a Server connection when you deploy the application using port 81 (the AdminServer) and the domain name of ADFDomain.
  36. When you deploy an app it should prompt to deploy to either the AdminServer or the managed server.

Programmatic Login From Faces with WLS

A while back I was having a lot of errm "fun", with trying to square the worlds of container managed security in OC4J with JSF based login screens. I won't bore you with the details, it was painful and never satisfactory as it either involved verbatim tags or fancy re-directs.
Anyway now that we've switched over to WebLogic for JDeveloper / ADF 11g it was time to look again and thankfully it's simple. Hurrah!
I'm working on a fuller sample for the forthcoming book but for now all you need this little snippet:

import weblogic.security.SimpleCallbackHandler;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;

...

public String performLogin() {
  String un = getUsername();
  byte[] pw = getPassword().getBytes();
  FacesContext ctx = FacesContext.getCurrentInstance();
  HttpServletRequest request =
    (HttpServletRequest)ctx.getExternalContext().getRequest();
  CallbackHandler handler = new SimpleCallbackHandler(un, pw);
  try {
    Subject mySubject =
      weblogic.security.services.Authentication.login(handler);
    weblogic.servlet.security.ServletAuthentication.runAs(mySubject, request);
  }
  catch (FailedLoginException fle) {
    FacesMessage msg =
      new FacesMessage(FacesMessage.SEVERITY_ERROR, "Incorrect Username or Password",
          "An incorrect Username or Password was specified");
      ctx.addMessage(null, msg);
    }
  catch (LoginException le) {
    System.out.println("Some other problem on login " + le.toString());
  }
  return null;
}

This lives in a request scope managed bean with the usual username and password attributes bound to your UI fields in JSF.
This works nicely with both Java EE Container and ADF (JAAS based) security.

Bindings Presentation

I've posted my data-bindings presentation here. Enjoy!

:: Next Page >>