Monday, 18 August 2008

4 things I read more recently (about REST)

I'm currently swamped doing non-blog stuff. But still I seem to take time out to read other blog entries, this time on REST web services. Ok, ok, I know, REST isn't strictly web services, that's why I'm reading ;)

Following are 4 posts about REST I checked out more recently that you might be interested in:
Hmmm, suddenly it doesn't feel like REST is the cool-kid on the block anymore.

Thursday, 14 August 2008

A presentation trifecta

I'm happy to announce that my presentation "Back to basics: Simple database web services without the need for an application server" has been accepted at 3 conferences, namely:

Oracle Open World (Session S301704) Monday 22nd Moscone West Room 2014

AUSOUG Perth conference 6th/7th October

AUSOUG Gold Coast conference 13th-15th October

I note the OOW Room 2014 holds 300 delegates, but that we're down to 294 available seats,so the rush is on!! Make sure you reserve your seat now before the two-hundred-and-ninety-four other seats go! ;)

Thanks to Oracle and OOW for running via the Mix the vote-a-session program and all those nice people for voting for me (hi Mum!); I'm appreciative of the chance to present on the official OOW agenda, as well as freak out in such a big room. Make sure you sit at the front so you can see me sweat.

I also be presenting the last leg of the JDeveloper masterclass in the Gold Coast, and helping out with the ADF Methodology at the OOW Unconference, so a busy conference schedule for me.

I'm very much looking forward to seeing and meeting Oracle enthusiasts both sides of the big blue bit. See you there :)

Thursday, 7 August 2008

JDev/ADF: How to log user login/logout/timeout to the database

I recently had a requirement to record when users log-in and out of my ADF application, as well as time-out of the application after an inactive period. As part of the requirement the user's name, login datetime, and logout/timeout datetime needed to be written to the database.

After much head scratching, staring at ADF BC help documentation to see if there was a solution, a case of Java brain freeze, I finally posted on the JDev OTN forum for assistance. Luckily John and Pako came to my rescue (thanks gents!) and gave me the inspiration for a solution. I thought I'd share my solution here for all to use.

My problem in looking for a solution was I was focusing on ADF BC and ADF Faces/JSF too closely to find a solution and ignoring the rest of the J2EE stack. Let's explain:

While ADF BC is ideal for working with the database, it is not ideal for capturing the start and end of user sessions. (This isn't such a surprise if you consider ADF BC works as an ORM, not a session management tool) The ADF BC application modules are only instantiated when you have a web page that reference's the AM and the VOs of the ADF BC project. If the user first navigates to a web page in your application such as a splash screen that shows no database derived information, the AM user session isn't started, missing the opportunity to record when the user session's started.

What about JavaServer Faces? Do JSF session level beans provide a solution? Unfortunately the same issue for ADF BC above applies to JSF session level beans. The JSF beans are only instantiated when an EL expression references them. If the first page your user navigates to doesn't include the appropriate EL, the JSF session bean is not instantiated missing the beginning of the user's session.

As such we need to step back and see what the J2EE stack can provide to us to solve this problem. The solution is 2 classes, a custom HttpSessionListener class and a custom Filter class.

J2EE applications can support creating a custom implementation of javax.servlet.http.HttpSessionListener interface. When configured within the project's web.xml file, on the start and end of any session, the custom listener's methods sessionCreated() and sessionDestroyed() are guaranteed to be called:

package view;

import // relevant imports

public class HttpSessionListener
implements javax.servlet.http.HttpSessionListener {
public HttpSessionListener() { }

public void sessionCreated(
HttpSessionEvent httpSessionEvent) { }

public void sessionDestroyed(
HttpSessionEvent httpSessionEvent) { }
}


And the web.xml entry looks like:

<listener>
<listener-class>view.HttpSessionListener</listener-class>
</listener>


What constitutes the start of a session? Whenever a new user accesses a web page within our application.

What constitutes the end of a session? Either when the user logs out of the session through a facility supplied by the programmer that calls the session.invalidate() method. In JSF this would be:

FacesContext fc = FacesContext.getCurrentInstance();
HttpServletRequest request =
(HttpServletRequest)fc.getExternalContext().getRequest();
request.getSession().invalidate();


....or alternatively the session times-out, configured by the web.xml <session-timeout> entry (expressed in number of minutes).

With the sessionCreated() and sessionDestroyed() methods within the HttpSessionListener, we now have an ideal place to write-and-read values to-and-from the implicit J2EE session scope to capture the user logging in and out. For example:

public void sessionCreated(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();

String sessionScopeUser =
(String)session.getAttribute("User");
if (sessionScopeUser == null ||
sessionScopeUser.equals("")) {
session.setAttribute("User", "?????");
}

Long sessionScopeLoginDateTime =
(Long)session.getAttribute("LoginDateTime");
if (sessionScopeLoginDateTime == null) {
session.setAttribute("LoginDateTime",
System.currentTimeMillis());
}
}

public void sessionDestroyed(
HttpSessionEvent httpSessionEvent) {

HttpSession session = httpSessionEvent.getSession();

String user = (String)session.getAttribute("User");
Long loginDateTime =
(Long)session.getAttribute("LoginDateTime");

Date loginDateTime = new Date(loginDateTime);
Date logoutDateTime =
new Date(System.currentTimeMillis());

SimpleDateFormat format =
new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
String strLoginDateTime =
format.format(loginDateTime);
String strLogoutDateTime =
format.format(logoutDateTime);

// JDBC to write the values to the database
}


For example you see in the sessionCreated() method that we can set a session variable LoginDateTime = System.currentTimeMillis, then in the sessionDestroyed() method we can retrieve the LoginDateTime and later write that to the database via a JDBC call.

However note an issue with the above solution. Within HttpSessionListener, we have no way to get at the authenticated user's details (essentially their account name). Unfortunately the HttpSessionListener doesn't have access to the Http request or response which does have access to the user's credentials, thus the limitation.

To get around this we introduce another J2EE construct called a javax.servlet.Filter. A class implementing this interface has a method called doFilter which is called on each Http request/response in our application. Within the Filter we are allowed to write to session scope, and in addition it has access to the user's credentials.

As such we can do something like this:

package view;

import // relevant imports

public class SessionFilter
implements javax.servlet.Filter {
public SessionFilter() { }

public void init(FilterConfig filterConfig)
throws ServletException { }

public void doFilter(
ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain)
throws IOException,
ServletException {

if (servletRequest != null &&
servletRequest instanceof HttpServletRequest) {
HttpServletRequest request = (
HttpServletRequest)servletRequest;
HttpSession session = request.getSession();

String sessionScopeUser =
(String)session.getAttribute("User");
if (sessionScopeUser == null ||
sessionScopeUser.equals("")) {
String user = request. (cont..)
getUserPrincipal().toString();

if (user != null) {
session.setAttribute("User", user);
}
}
}
filterChain.doFilter(
servletRequest, servletResponse);
}

public void destroy() { }
}


Note if the current session scope User variable is null, we grab the principal user from the request and write this to the session scope. Thus when the sessionDestroyed() method is called it has an actual User to work with.

To configure the Filter in the web.xml file you include the following entries:

<filter>
<filter-name>SessionFilter</filter-name>
<filter-class>view.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>


Now the listener can grab the user name from the session scope supplied via the filter. This completes the solution.

There are a couple of caveats with this solution:

The user's credentials (account name) can only be retrieved by the Filter if the user has or is visiting a J2EE container based protected page of the application. If the user has yet to visit a protected page and thus hasn't been forced to login, the user's credentials will not yet be initialised. As such in creating an application where the user's details are recorded on entry and exit to the application, the user must be forced to log in regardless of which application web page they access. This is simply at the programmer's discretion depending on how they developer sets up the container based security.

(Note however at some point if the user does login, the filter will correctly pickup the user's name at that point, and that will be used by the end listener.sessionDestroyed() method thereafter defeating the caveat above. Also note there appears to be a difference between what the call to request.getUserPrincipal() returns in a number of cases when the user isn't authenticated. I believe if the app isn't configured against an LDAP security provider, it'll return null, but otherwise "anonymous" in the case of an LDAP security provider. Please ensure to test this functionality in your environment).

In testing this facility, you might notice if you force the user to logout via the invalidate() method described above that the HttpSessionListener sessionDestroyed() method is called twice, once when they logout, and a second time after some delayed time period. This occurs if after the logout you redirect the user back to a web page within your application. What you're essentially doing is starting another session (which may not be immediately obvious if you haven't added security/authentication requirements to all your web pages), and the delayed second call of the sessionDestroyed() method is a timeout occurring. The simple solution, on logout redirect the user to a web page outside of your application.

The solution above assumes you'll write the session information to the database via a JDBC call creating its own database connection. Ideally in an ADF BC project, you'd make use of the connection pool provided by ADF BC rather than creating wasteful dedicated database sessions.

A final note is the above solution is just that, and not necessarily the best way to do this. This code has not been tested, has not been run in a production environment, and therefore your mileage may vary. You have been warned! However hopefully this will be helpful to someone, or alternatively somebody will see the error in my ways and tell me a much better way to do this!

Tuesday, 5 August 2008

The 1st OOW08 Unconference session! - An ADF Methodology for the masses

2008 sees Oracle continue to expand their Oracle Open World offerings, with a greater participation from delegates as well as the usual "speaker mob". Last year Oracle and specifically OTN offered the Unconference for the first time, a great chance for anybody "off-the-streets" to sign up and present on any topic they felt interesting.

I'm excited to announce that the OOW08 Unconference is up and running and the ADF Methodology is the first cab out of the ranks.

As many of us know, a software development methodology is designed to assist experienced and uninitiated technology practicionist standardise their approach to software design & development. A methodology can help highlight common decision points, outline best practices, promote code reuse, and propose standard deliverables among many other things.

The goal of the ADF Development Methodology is to propose just such a methodology for JDeveloper Application Development Framework (ADF) based projects. With the experience of real-world experts, including Oracle ACEs and Oracle staff, we'd like you to join us to put such a methodology together.

Obviously a methodology is a huge undertaking. The intention of the OOW08 Unconference session is not to define a complete methodology at this time, but begin the process of constructing the methodology, to be filled in and expanded upon at a later date. All proceeds from the Unconference session will be placed on the Oracle Wiki allowing all to access the outcomes for their own use.

If you're interested in attending, it would be great if you could register your interest to assist us in planning please.

And as Shay Shmeltzer recently blogged, we've also started a Google Group to discuss the methodology before and after the event. Feel free to check it out and join, all participants welcome.

Thanks to Justin Kestelyn and the OTN team for their assistance behind the scenes, and to Shay for his blog post.