I'm not sure if what I'm doing is wrong, or if I just missed an annotation or configuration item somewhere. Here's the situation:
I have a JSF application with a session-scoped bean named SessionData
. This bean has an application-scoped bean reference (of type ApplicationData
) injected into it at creation time. This works ok when the session is first created. The dependency injection is done with <managed-bean>
elements in the faces-config.xml
file as shown here:
<managed-bean>
<managed-bean-name>sessionData</managed-bean-name>
<managed-bean-class>my.package.SessionData</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>applicationData</property-name>
<property-class>my.package.ApplicationData</property-class>
<value>#{applicationData}</value>
</managed-property>
</managed-bean>
<managed-bean>
<managed-bean-name>applicationData</managed-bean-name>
<managed-bean-class>my.package.ApplicationData</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
Because it doesn't make sense to have my SessionData
object include the ApplicationData
object when it is serialized, I have marked the ApplicationData
reference as transient in my SessionData
object:
transient private ApplicationData applicationData;
All is good until the web application is stopped (in my Tomcat 6.x container) and the sessions are serialized. When I restart the application and the sessions are deserialized, my reference to ApplicationData
is not re-injected by JSF. I know that deserialization is supposed to leave transient fields without a value. Is there a way to signal JSF that this session-scoped object requires its dependencies be set again after deserialization?
I am using MyFaces JSF 1.2 and Tomcat 6.0.26 as my web application container.
you can add a method:
And in
initializeApplicationData
you can use a dynamic proxy object. Using CGLIB or javassist create a proxy that, before each method invocation sets an internal field - the realApplicationData
. If it isnull
, then get the currentFacesContext
(which will be accessible at that point) and get the managed bean from there by:and delegate to its methods.
This is an ugly workaround, but I think it will work.
Although the solution offered by Bozho could work, I don't want to introduce proxy objects into an application that isn't currently using them. My solution is less than ideal, but it gets the job done.
I left the transient field in place:
I also left the setter in place so JSF can initially set the reference when the
SessionData
object is created the first time:The change that I made was in the getter method. Methods in the
SessionData
object now need to stop directly accessing the_applicationData
field and instead get the reference via the getter. The getter will first check for a null reference. If it is null, then the managed bean is obtained via theFacesContext
. The constraint here is that theFacesContext
is only available during the lifespan of a request.If anyone cares, here is the code for my
getManagedBean()
method:And don't pick on my Validate calls. I'll take them out after I'm done with development! :)