A JSF Session Scope Custom Solution

by on January 23, 2012 12:59 pm

As a software consultant, I very rarely end up in a situation where I am asked to implement something new from scratch, and even rarer is the request to come in and enhance an existing piece of code that is well-designed and beautifully written. The request I received on my last project is much more the norm:

“We have several applications written in JSF using session scope.  Last release we added an application that can be used to launch multiple instances of any or all of these session scoped apps.  But our problem is that multiple instances of the same applications interfere with each; they seem to be sharing data. Please help us.”

Problem Explanation

Just in case the problem here doesn’t leap out the web page and grab you, just yet, here is the problem in a nutshell:

Web applications can be designed so that everything you want the server to know is passed on every request. For example, in a user registration application you could be prompted for name, user id, and password on one page and then prompted for security questions on the next page along with a submit button. The submit button is going to have to give the web application all of the registration information needed to register a user, so all the data from the first page had to be passed to the second page and then combined with the second page prompts on the submit. As you can easily imagine, a shopping application that may have hundreds of pages would get very complicated passing around your shopping cart on every page until you clicked “checkout”. This is where a session comes in. The shopping cart is a great metaphor for session as it is the thing that holds all YOUR stuff until you are ready to checkout. “YOUR” is emphasized because each web user gets their own cart/session.

The problem above is that the applications in question were written to use session, so each user gets their own session for carrying around all the information for the app. However, launching independent instances of the same application by the same user means all instances for one user share a session. Using the metaphor above, you can think of shopping for separate people but having to use the same cart for everyone’s stuff.  In the real world, you can make that work, but in the web world there can be problems. If the application was written to ship to only one shipping destination or if the application limits the quantity on an item for each purchase, then treating each shopping “instance” as a separate purchase, that is, using a separate session per purchase would work fine.  You’re probably thinking, though, why not just change the application to allow multiple destinations or to allow a single purchase to be treated as multiple.  If you are thinking this way, then you are at the perfect point to see the problem.

The Catch

When our client asked us to help, there was a catch (as always there is). “We don’t have time to rewrite the application or even make enhancements. In fact, we don’t know specifically which use cases are even broken.” So, here we were with the real problem.

How do we just fix it without breaking anything else in the application? The real problem is that the applications were not designed to support multiple instances per session, but we needed a way to do it in JSF 2.0. The first approach, the one proposed by the client’s architect, was to use ViewScoped managed beans instead of SessionScoped. This would be a good answer since ViewScoped is per view, for example, each path through the shopping experience, and thus would avoid the problem altogether. But, of course, there is a catch; this requires you to write your application in a way that ViewScoped beans will work. Unfortunately these applications were not written that way. The other problem with this approach is that for some reason, using ViewScoped beans on pages that are still using the JSTL “c:foreach” tags causes major performance problems because each item being iterated gets its own scope and that isn’t what we wanted at all.

The Solution Process

Okay, so the applications were written in a way that expected there to be a session per instance, but the application server dictates that there is one session per user, not per application instance. JSF provides a request scope, but that won’t work because we don’t pass all the data on each request. We already know session scope won’t work. JSF 2.0 added view scope which also won’t work. But there is one more JSF scope (new in 2.0) that might work. It is called Custom scope. But as the name implies, you can make it do whatever you want it do, but you have to do the work. In our case, we did need something custom. We wanted it to behave just like session scope except that each time the same user launched a new instance of the application we wanted the application to act like we had our own separate piece of the session. Ah ha! What we wanted was a new scope called PartitionedSession scope. Each instance of the application would get its own session-like “cart” for holding all its information. The trick is how to make that work without rewriting the applications.

So the first part of the solution is implementing our new custom scope behind the scenes.

The basic premise behind JSF’s component-based architecture is that each time a page is rendered, whatever beans are referenced on the page are instantiated and populated, the page is displayed, and then the beans go away. The beans are instantiated by a class called an ELResolver. This resolver class looks at the scope of the bean to determine how to instantiate or find it. For request scope beans, the beans are always instantiated and initialized with request parameters. For session scope beans, the resolver looks in one place in session to see if a bean by that name already exists. If it does, that saved bean is used, otherwise a new instance is instantiated. View scoped beans are a little more complicated in that they depend on a view id being in the context so that the resolver knows which view scope to use. This design makes custom scope a cinch.

For JSF custom scope, you actually use a custom resolver. The custom resolver implements the normal ELResolver methods and is chained into the normal set of resolvers that are a part of the JSF framework. When a bean reference is being resolved, this custom resolver gets a chance to see if the custom scoped object is bound to already has an instance of the bean. In the SessionScoped case, a single hashmap per HttpSession is used to hold all the bean instances. Our custom resolver would need a way to have a separate hashmap per Partition. As long as the resolver has a way of knowing which partition to check, then it becomes a simple matter of picking the correct partition hashmap out of session. The solution to this part of the problem is to rely on a PartitionId. This id must be available to the resolver on every call, but given that assumption, the custom ELResolver is a very simple implementation of a hashmap lookup.

So, how do we ensure we always have a PartitionId?

Before answering that we must determine when to start using a PartitionId and when to stop using it. In our client’s scenario, any time an application is launched we want to get a new PartitionId and use it throughout that use of the application. In this case, a Partition life time is the same as that of the browser window it was opened into.  There was some debate on whether to use the term WindowId instead of PartitionId, but since this is a server solution, PartitionId was chosen. For all intents and purposes, there is a one-to-one correspondence of PartitionId to browser window. So the trick is that every time an application is launched, the server needs to know to create a new unique PartitionId. Every subsequent request made from that browser window needs to pass in that same PartitionId to let the resolver know which partition to use.

Without going into all the details, it was decided that it is simplest to treat the case where there is no PartitionId on the request as the case where we are launching an application. In this case, the custom resolver will ask a PartionedScope service to generate a new unique PartitionId (unique for this user) and will then use this new id to create a new hashmap in session for this new partition. Once again I’ll point out that the custom resolver is very simple.

The difficult part is how we ensure that that PartitionId will come in on every subsequent request from that window. The problem is that when a response is rendered to a screen, it contains a number of user input controls that allow the user to call back to the server.  The most obvious controls are buttons and links. For instance, in our shopping cart example, after adding something to the cart, the user may see a button for “Check-out”, a link for “Shipping Options”, images for previous and next page, and many images for shopping items. All of these cases cause a new request to the server and to use the information stored in our session partition, we must pass the PartitionId on every possible link. This problem is further complicated by AJAX requests that can asynchronously update the page.

Using a common practice of adding a hidden field to a page to carry information from page to page would be very time-consuming, tedious and error-prone.

We needed an alternative that would be done programmatically.

As with all successful solutions, we “re-used” an existing implementation. As it turns out, this problem had already been solved by two existing JSF frameworks; JBoss Seam and MyFaces Orchestra. Each of these open source frameworks used the term Conversation rather than partition, but both faced similar challenges and both came up with similar solutions. The solution we borrowed was to use a Servlet filter to override the encodeUrl call. This call is made when rendering the response to ensure that every link on the resulting response will have a URL that is properly “escaped” so that spaces and other special characters don’t interfere with the html parsing of the resulting page. Using this call as a “hook”, we were able to add the PartitionId, regardless of whether it was created or passed in on the request, to every link on the response page. Again, this solution is very clean and simple, avoiding the need to change any application pages.

As simple as this approach sounds, it didn’t come to us instantly. Also, this wasn’t the only requirement that required ingenuity. Another closely related requirement was how to consistently handle session timeout. It too required a servlet filter solution and in the end was combined with the PartitionedScope solution since both solutions were really just hacks needed to hold the application together until the application could be re-designed to support these requirement correctly.

Final Thoughts

In the end, our approach went from requirement specification to implementation in a very short four week sprint.  This challenge of making the application behave in a way that it was not designed to handle without any pervasive application changes was a difficult one, but not that uncommon.

I want to thank Josh McKinzie for his expert participation in this solution, since without his determination, perseverance and skills, this solution would not have been possible, especially in the time frame given. While there was a lot of stress and long hours required for our success, in the end it was an enjoyable experience that I had a lot fun doing. Satisfying nearly impossible requests from our clients is what makes our jobs rewarding.

– by Keith Shakib, asktheteam@keyholesoftware.com

  • Share:

8 Responses to “A JSF Session Scope Custom Solution”

  1. zhang ye says:

    Writeup is very nice, but why not just use existing conversation scope? (CDI, CODI, Orchestra)

    • Keith Shakib says:

      Changing the technology stack of the application was not an option given the time constraints and retesting requirements. They were already using JSF 2.0 and Primefaces and were not really open to changing out the frameworks.

      • zhang ye says:

        Oh, but please know, there no need to changing frameworks! Conversation scope from CDI useable with any JSF implementation or framework.

        Yes, we use same stack (JSF 2.0 (Mojarra), PrimeFaces), but before JSF 2.0 and RichFaces.

        When on GlassFish, JBoss, TomEE, Resin… no need to do anything, since conversation scope already there. When on Tomcat, or Jetty, you need only add 1 jar and CDI is there (please refer to http://www.theserverside.com/tutorial/Working-with-CDI-and-JSF-20-on-Tomcat-7-Configuring-Weld)

        Please, don’t think CDI is swapping framework. Only way how you create managed beans becomes little different. Is now @Named, instead of @ManagedBean. And only needed for pages needing conversation scope.

        Hope is clear ;)

  2. avgjoe says:

    Really great post! A couple of questions. You say that ViewScope would have helped you but I am wondering how ? ViewScope would only solve the problem of being on one page and not the case of maintaing state across a workflow (multiple stages) or conversation .. is that not correct ? The only viable solution is probably what you put foward (short of using a framework). Conversation scope should be something that is bundled with Vanilla JSF. I think Spring is also coming out with some basic out of the box support for this with thier next release (and think it’s called windowId).

    • zhang ye says:

      >Conversation scope should be something that is bundled with Vanilla JSF.

      Yes, I agree much!

      Little problem in Java EE 6 of JSF was done before CDI (maybe, CDI little bit late). Now many things in Java EE 6 have own managed beans (JSF, JAX-RS), but could be CDI.

      Mr. Ed Burns (he made JSF), told us maybe deprecate JSF Managed Beans for next version. Some time ago, on Twitter someone made vote for this and many people vote for “yes, please deprecate jsf managed beans for CDI).

      Developer of JAX-RS said about same thing I think.

      If JSF uses CDI standard, then conversation scope automatically present :)

  3. avgjoe says:

    Finally, one last question. What happens when I send the partition id (from my url) to the person sitting across from me ? I suspect that person will bring up my session info ..correct ?

    • Keith Shakib says:

      @avgjoe, you are right, ViewScoped really only helps on postbacks, so it depends on whether you can design your app that way to take advantage of it.

      Yes, if you read about conversation support in JBoss Seam that say it very well that most web applications are designed as a conversation, so conversation scope should be part of base JSF and will be.

      As for sharing partitionId, the answer is no. Remember this is just a partitioning of YOUR httpSession. Only your one browser instance can access your httpSession; the partitioning just lets you isolate state within that session. If you have, say, five partitions and go to lunch, you will still lose all five partitions if your session timesout.

      Hope this helps.

  4. [...] January, I wrote a blog post about the need to partition HttpSession across multiple browser tabs or windows. In that blog, the [...]

Leave a Reply

Things Twitter is Talking About
  • Tech Night is now! Mark D is presenting to the group on #Grunt 101. Good technology talk, food & team makes for a fantastic night.
    April 23, 2014 at 5:10 PM
  • Single Page Application architectures allow for rich, responsive UIs. #JavaScript is a must-know for SPA - http://t.co/6sfk3kt1k3 #tutorial
    April 23, 2014 at 2:15 PM
  • Vacuole #Encapsulation aims to minimize the code necessary for routinely verbose data tasks -http://t.co/fJQzz731rZ
    April 23, 2014 at 9:45 AM
  • DYK? We translate our hands-on experience to custom courses to train your dev teams. Our new course on #AngularJS: http://t.co/Bf3UuClj4Z
    April 23, 2014 at 8:45 AM
  • Interested in #Backbone & #Marionette but not sure where to start? Check out the Marionette-Require-Boilerplate: http://t.co/XDj43BwSS3 #SPA
    April 22, 2014 at 4:50 PM
  • Responsive Design can help in giving your users a consistent app experience across devices. Quick tutorial - http://t.co/BDrT5LvgRo
    April 22, 2014 at 2:31 PM
  • Tips & tricks to save time in the #Eclipse IDE - http://t.co/uGgCkchwNk (keystroke combos, navigation, time tracking & more)
    April 22, 2014 at 8:40 AM
  • Join us! Looking to add to our team a developer w/ advanced #JavaScript & #NodeJS exp (& love of tech variety). Info: http://t.co/cC9CU1RCF9
    April 21, 2014 at 7:35 PM
  • Looking into #ExtJS but don't know where to start? Check out our video tutorial series to find your way around - http://t.co/XFYDT6YNWA
    April 21, 2014 at 4:35 PM
  • We've been tinkering with JS library Famo.us since its public release 4/10. What we've learned so far via a POC app - http://t.co/S77TSKHDKd
    April 21, 2014 at 2:03 PM
  • RT @CompSciFact: Rivest, Shamir, and Adleman published the RSA public key cryptography algorithm in 1978.
    April 21, 2014 at 11:13 AM
  • DYK? When we share/RT/blog/etc, it doesn't mean that Keyhole endorses it - we just like variety of opinions! Info: http://t.co/MXhB9lE9tV
    April 19, 2014 at 3:01 PM
  • A huge welcome to Justin Graber who joined the Keyhole team this week!
    April 18, 2014 at 3:25 PM
  • Pssst... @kc_dc early bird pricing ends on Sunday. Shoot us a note if you want to save 10% off of your ticket with our sponsor promo code!
    April 18, 2014 at 2:49 PM
  • Join our team! Looking for a developer w/ advanced #JavaScript & #NodeJS experience (& love of tech variety). Info: http://t.co/cC9CU1RCF9
    April 18, 2014 at 11:21 AM
  • .@befamous has huge potential to make HTML5/JS/CSS web pages feel as native apps. Here's our inital tech takeaways - http://t.co/S77TSKHDKd
    April 18, 2014 at 9:50 AM
  • Why to use AngularUI Router instead of ngRoute - http://t.co/tBnj5ZCkOw
    April 17, 2014 at 7:55 PM
  • RT @joemccann: Total Number of GitHub Repositories by Programming Language http://t.co/30cekDsE4s
    April 17, 2014 at 4:25 PM
  • JSF + AngularJS = AngularFaces? http://t.co/mXvOTwVbb6 // Interesting insight. Thoughts?
    April 17, 2014 at 3:45 PM
  • RT @MikeGelphman: Great news, guys: @TobiasRush founder of @eyeverify is our latest @MobileMidwest speaker addition http://t.co/8fE8oNfPnX
    April 17, 2014 at 1:35 PM
Keyhole Software
8900 State Line Road, Suite 455
Leawood, KS 66206
ph: 877-521-7769
© 2013 Keyhole Software, LLC. All rights reserved.