Friday, May 20, 2011

JSF sharing across multiple WARs in JBoss/Tomcat

This will be where I keep notes until I have a final solution. Then I'll reformat.

If someone decides that your application should be split up between more than one WAR, but they need to share some data such as User information (even just a logged in token or something), you have to find your own solution as this is not supported by the J2EE spec.  So far I've decided to do some sharing by making one of the WARs (we'll likely have 4 by the end) the one that holds the shared content.  I'll call it 'war1'.  In JBoss/Tomcat, this will give it a context name of '/war1'.  So, in 'war2', we need to look up the /war1 context and grab info out of its session.  I'll give some more detail.

In JBoss/Tomcat, there is a property in the server.xml called crossContext.  Depending on your JBoss/Tomcat version, it may be split into separate config files.  For example, in my JBoss 5.1, the actual config file is C:\jboss-5.1.0.GA\server\default\deploy\jbossweb.sar\context.xml.  You'll need to set this property to true.  The default is true in JBoss 5.1.  I don't know about other versions or standalone Tomcat.  Setting crossContext=true will allow us to lookup contexts from other contexts.

To set something in the shared context, I've done this: (this is in war1)

HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
request.getSession().getServletContext().getContext("/war1").setAttribute("shared", shared);


In war2, we get it out similarly:

HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
ServletContext medicalportal = request.getSession().getServletContext().getContext("/war1");
Object s = medicalportal.getAttribute("shared");


Ideally, I would like to have a context dedicated to sharing: /shared.  Then all wars would go to the same spot for sharing.  This is all in an EAR; so, we have a jar with some JSF code.  We can shove the shared context accessing code there so that we can have code like this:

FacesUtility.setSharedAttribute("name", value);
Object value = FacesUtility.getSharedAttribute("name");


An over-the-top solution but way cool would be to have a JSF variable resolver that would look in the shared context for beans.  ...  Actually, that might not be over the top.  It would make the code much less complex.

3 comments:

  1. It's worth mentioning that this will only work for global variables that apply equally to all users of the web application. If you need to share session-scoped information between contexts, you need a different solution. In my own situation, GET parameters added to the URL fit the bill.

    ReplyDelete
  2. Thanks i used the solution on JBoss AS7 but only that in both application i have a jboss-web.xml that has the property disable-cross-context set to false and it worked.
    false

    Thanks for the heads up

    ReplyDelete
  3. @Laszlo:
    You can store user/session specific data using the same approach as described above. You would store session data in something like WeakHashMap (or similar) based on session or user ID.

    Since you have multiple sessions for the same user (multiple wars), you need one war (/shared) to keep track of user data. Based on ID. And update its context instead of relying on local session in each war.

    Or you could store all session IDs.

    You could also use Filter to keep everything in sync and hide this logic from the rest of application/business logic. Filter would catch each http request and manipulate both, existing http request (possible session too) and shared web context.

    This will create nice, custom and simple SSO solution implemented by yourself and for your own needs. You have full control of it.

    BTW, there is no magic in this and many SSO implementations are doing exactly that but just more complicated way so they can support all clients not just one (as in your case).

    Good luck.

    ReplyDelete