Monday, July 15, 2013

@TransactionManagement doesn't work in JBoss 6 Final

Only for the record. Here it is a link of a post in the Jboss community about a bug I've found. This cause me some headache. ;)

https://community.jboss.org/message/588983

Here it is the bug filled: https://issues.jboss.org/browse/EJBTHREE-2238

Friday, March 8, 2013

JAAS Persistent sessions with JBoss 6

A common issue you can found when your web application is becoming more popular is how to update it without disturbing your users.
Redeploying a standard application could take arround 5-10 secs in a JBoss6, so we could assume that down-time for a monthly basis update.

But, what happens with logged-in users when you update your application? Well, they will loose their session and will need to login again. We could assume that for a couple of redeploys, but at the end you will notice that you are very reticent to update your application because your users will suffer it.

If you look at google for jboss persistent sessions, you will found that it is so easy solve this problem. In most tutorials you will be encouraged to save the session data to disk, so when you redeploy your application, the session will not be lost. Configuring jboss for that is very simple: In server/default/deploy/jbossweb.sar/context.xml you will see a comment saying that session persistence is disabled and what you need to enable it is uncomment the following line:

  <Manager pathname="SESSIONS.ser" />

Now, the session will be serialized to that file, you should take in account that your session data must be serializable, but in most cases it is. You could even change the path of the SESSIONS.ser since it is saved on a temporary directory and it will be deleted on jboss restarts. To avoid this, simply set an absolute path:

  <Manager pathname="/opt/app/data/SESSIONS.ser" />

That is all, so easy isn't it? Well, but... This doesn't work if you are using JAAS to authenticate your users. The reason is because the session is stored correctly but the JAAS principal is not, and JAAS will discard the session for users without a principal. So at this point, you have two options:
  • Not use JAAS as authentication framework.
  • Assume that your users will loose their session when you redeploy your application.
  • Or...
Create a jboss cluster. I haven't evaluate this option before because seemed I was complicating a lot the infrastructure for a simple web app. But I was really frustrated by the fact I was unable to update the application without disturbing my users. So I decided to give it go.

I starting reading a couple of tutorial on how to setup a cluster and seemed to be very easy.
Since my intention with the cluster was only maintain the users logged in when redeploy my application it's required only two nodes on the cluster. If I use two ethernet interfaces I could maintain the two nodes in the same server, simplifying the overhead of this configuration.

This is the recipe I've used to configure two nodes of a JBoss Cluster to avoid JAAS principal loss:

Changes in the application

We don't need to change any code on our application, simple add this line to the web.xml to inform to jboss that the application is clusterizable:
        <distributable />
In most cases you need to do nothing else in your application.

Changes in the server

Simply configure a virtual ethernet interface assigning an additional IP:
For ubuntu/debian, in /etc/network/interfaces

auto eth0
iface eth0 inet static
        address 172.26.1.1
        netmask 255.255.255.0
auto eth0:2
iface eth0:2 inet static
        address 172.26.1.2
        netmask 255.255.255.0

Configuring the JBoss

There is not so much to configure, create two separate installations of jboss, for example:

/opt/jboss-node1
/opt/jboss-node2

And use the following run scripts for each one:

/opt/jboss-node1/bin/run.sh -c all -b eth0 -g <cluster_name> -Djboss.messaging.ServerPeerID=1 -Djboss.jvmRoute=<node1_name>
/opt/jboss-node2/bin/run.sh -c all -b eth0:2 -g <cluster_name> -Djboss.messaging.ServerPeerID=2 -Djboss.jvmRoute=<node2_name>

You must replace the values between <> with your specific values.
Cluster_name Is a name you want to give to your cluster. Since the cluster will autodiscover all nodes in the network with the same cluster name, you will want to have a special name to separate dev and production clusters in the same network.
The ServerPeerID and node_name are unique for each node, you can select whatever you want.
The jvmRoute name allows us to associate all request from a specific user to the same server. In server/all/deploy/jbossweb.sar/server.xml locate the line:

 <Engine name="jboss.web" defaultHost="localhost">

and change it as:

 <Engine name="jboss.web" defaultHost="localhost" jvmRoute="${jboss.jvmRoute}">

Finally, you need to enable the ClusteredSingleSignOn valve in jboss.web to activate the JAAS principal propagation. Go to server/all/deploy/jbossweb.sar/server.xml and uncomment the line:

<Valve className="org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn" />

Now, you can deploy your application on server/all/deploy, this should work as usual.

Configuring the balancer

If you have deployed your application on both servers  you will be able to reach your application by pointing your browser to 172.26.1.1 or 172.26.1.2 On that point, you only need to balance the incoming users to each node, and of course if one of the nodes goes down the users on that node will be moved to the other node. And the most important: The users won't notice the change.

The balancer I'm using is mod_jkyou can install it on debian/ubuntu with:

apt-get install libapache2-mod-jk

Then, proceed as usual to enable the module on your apache-httpd.
Configuring the mod_jk to register the nodes on the cluster and how will be reached:

workers.properties:
worker.<node1_name>.type=ajp13
worker.<node1_name>.host=172.26.1.1
worker.<node1_name>.port=8009
# worker.<node1_name>.fail_on_status=404

worker.<node2_name>.type=ajp13
worker.<node2_name>.host=172.26.1.2
worker.<node2_name>.port=8009
# worker.<node2_name>.fail_on_status=404

worker.mycluster.type=lb
worker.mycluster.balance_workers=<node1_name>,<node2_name>
worker.mycluster.sticky_session=1

As you can see, we have defined our two jboss nodes, and a cluster called mycluster that will balance the incoming requests alternatively to both nodes node1 and node2. If a node is unreachable it will be marked as down and won't receive requests anymore until becomes active again. We can also use the fail_on_status to force a node status down when the node returns the specified error codes. (can be a comma separated list).
The sticky_session will maintain the users in the same server.

Now, we can mount mycluster to a specific url on our apache-httpd with the following configuration. You can put it in your virtual server configuration:

    JkMount       /yourapp   mycluster
    JkMount       /yourapp/* mycluster

As you can see, we have made available our cluster in the http://apache_url/yourapp url. If you access to that url, mod_jk will redirect you to any of the two server nodes. If that node fails it will redirect to the other node.

The advantage here is the jboss cluster will maintain http session and JAAS principals in all cluster nodes, so if one node goes down, the other node can maintain the users authenticated.

I've used this configuration for a while and works really well, and doesn't requires more maintenance or attention that a simple jboss.