We have a web application that is using Spring Boot (1.5) with Vaadin (7.7), and is using Apache Shiro (1.4.0) for security.
The application is configured to use DefaultWebSessionManager
to let Shiro handle the session management instead of the servlet container.
We are using the official Vaadin Spring integration (1.2.0), and after some configuration it all works as intended. The VaadinSession
contains a wrapped ShiroHttpSession
internally.
We want to achieve session replication, by configuring Shiro to use a SessionDAO
that is backed by an external Cache
, which means the sessions get (de)serialized.
As soon as we start using this SessionDAO
, Vaadin will crash and stop working. When replace the external cache by an in memory Map
for the sake of debugging, it works again.
It seems this is caused by the SpringVaadinServlet
, as it stores the VaadinSession
as a session attribute. VaadinSession
is Serializable
and the Javadoc shows:
Everything inside a VaadinSession should be serializable to ensure compatibility with schemes using serialization for persisting the session data.
Inside the VaadinSession
are some fields that are not Serializable
, for example a Lock
and the wrapped http session inside is also marked as transient.
Because of this, the session that Vaadin uses will be broken as soon as it is distributed, resulting in a lot of crashes.
So it turns out the VaadinSession
is not actually usable in session replication? Why is this and how can we work around this?
Note: we also have a version of the application that is using Vaadin 8, and here the same thing happens. It seems that the issue is caused by the Vaadin Spring integration.
Inside the VaadinSession are some fields that are not Serializable, for example a Lock and the wrapped http session inside is also marked as transient.
The wrapped http session is not part of Vaadin session, it is the the http session. Thus it is transient. The same can be said about Lock, whose instance is stored in the http session.
In order to implement session serialization correctly, you need to hook into serialization events and update the transients when session is being deserialized. VaadinSession
should be loaded with VaadinService#loadSession
, which calls VaadinSession#refreshTransients
.
Everything inside a VaadinSession should be serializable to ensure compatibility with schemes using serialization for persisting the session data.
This statement does not imply that you can serialize your application out of the box. It just means, that in case your application is serializable as well, with careful engineering you can serialize the whole thing.
For example Vaadin is not updating the session attribute in each possible occasion for performance reasons. There is method VaadinService#storeSession
for that. So you need to either override right method or setup request filter. E.g. you could do this at VaadinService#endRequest
.
Note, you need to use sticky sessions in order to get this to work with moderate amount of effort. If your session is de-serialized in different machine, the re-entrant lock instances wont be valid. If you would like to be able to de-serialize the session in different machine, it would require that your infrastructure can offer distributed lock that you can use instead of re-entrant Lock of Java and override Vaadin's getSessionLock
and setSessionLock
methods to use that.
Valuable sources of further info:
Generic notes from Vaadin's CTO
https://vaadin.com/blog/session-replication-in-the-world-of-vaadin
Testimonial from developer who did it with one stack
https://vaadin.com/learn/tutorials/hazelcast
Thoughts from another senior developer