JSF 2.1 ViewScopedBean @PreDestroy method is not called
Asked Answered
D

2

13

I have a method in a view Scoped Bean with the @PreDestroy annotation and another one with the @PostConstruct annotation.

The @PostConstruct method is properly called every time I navigate to the page that uses this view scoped bean.

However, when I navigate to a new page (which doesn't use this view scope bean) by <h:link/>, the @PreDestroy method is never called.

I am not talking about changing manually the url or the end of the session, just of a navigation case.

What I am missing?

Thanks in advance

Dolichocephalic answered 17/7, 2011 at 20:0 Comment(0)
D
20

This is by design. It will only be destoyed immediately when a POST action results in a navigation which isn't a postback to the same view (i.e. the action method didn't return null or void, but a fullworthy String, even when just empty).

The <h:link> generates a GET link which doesn't invoke any POST action. It's with currently available technology not reliably possible to notify the server side when the view is unloaded, so JSF can't be notified to destroy the view scoped bean associated with the view. In such case, the view scoped bean will then only be destroyed when the session expires or when the max logical views in session has exceeded (the default is 15) and the associated view is the first in order.

If you really want to destoy the view scoped bean by a navigaiton action, then your best bet is to make it a POST request by <h:commandLink> instead and issue a redirect by returning the navigation outcome with ?faces-redirect=true parameter. But this is after all not SEO friendly as bots won't index POST links.

In theory it would be possible by HTML DOM onbeforeunload event, but this is currently still a non-standard event and the browser behaviour is unspecified as to what happens when you send an ajax request during that event. It will sometimes arrive, but sometimes also not. JSF as a spec can't depend on non-standard specifications and hence it's never implemented from JSF side on. But it should be possible with a 3rd party JSF library which doesn't care about non-standard specifications.

Update: in practice, this has been implemented in OmniFaces @ViewScoped since OmniFaces 2.2. Initially with help of synchronous XHR and since OmniFaces 2.6 with help of beacon (with synchronous XHR as fallback). It's working quite well in major browsers. Since OmniFaces 2.3 it even immediately destroys the associated JSF server side view state, and since OmniFaces 2.6 it also immediately destroys the physical beans, hereby further reducing unnecessary memory usage. See also among others JSF: Mojarra vs. OmniFaces @ViewScoped: @PreDestroy called but bean can't be garbage collected

Denti answered 5/1, 2012 at 16:43 Comment(3)
A small correction is in order. Even if you do a redirect, if you redirect to the same view as where the POST request originated from, JSF will not destroy the view scoped bean. For a view scoped bean to be destroyed, you will need to render a different view entirely after the POST request is processed.Holmquist
If I understand well, with a beacon the @PreDestroy of the leaving view might be called after the @PostConstruct of the newly loaded view?Anglo
@ForguesR: yup, it's asynchronous from each other.Denti
A
1

I have prepared a small NetBeans project demonstrating when JSF2.2 CDI-compatible @ViewScoped beans (javax.faces.view.ViewScoped) are released for garbage collection under different navigation cases (for Mojarra 2.2.9, Glassfish4, NetBeans8.0.2, JDK1.7), available for download here. The code is omitted here, please see that download.

The navigation cases handled and the results are summarised by this image:

Image showing index page using @ViewSCoped bean with JSF navigation cases to a done landing page

To monitor the @ViewScoped beans, use VisualVM against Glassfish (or the in-built NetBeans profiler on the mini project) and filter the Sampler memory Heap histogram class view on the package name 'webel.com.jsf'. The following image shows an absurd 66 instances of webel.com.jsf.Jsf22ViewBean after copious experimenting with h:link, browser URL GETs, and browser RELOAD GETs, which instances will not be garbage collected (which you can test using the VisualVM Perform GC button):

enter image description here

By comparison, navigating away from the index.xhtml (which uses the @ViewScoped bean once for a simple EL variable read) to done.xhtml (which does not use the bean at all) using h:commandButton and an action method expression or an action string results in the @ViewScoped bean being released for garbage collection (there is always one reference from the WeldClientProxy to a @ViewScoped bean, and as you navigate back and forth using h:commandButton the WeldClientProxy moves from one releasable bean to the next).

Acceptable answered 23/5, 2015 at 8:3 Comment(1)
The above answers the question (further builds on BalusC's answer) by exhaustively demonstrating the various navigation cases, but it does not explain what to do about the resulting "dangling" @ViewScoped beans that won't garbage collect: I ask that as a question here: stackoverflow.com/questions/30410601/…Acceptable

© 2022 - 2024 — McMap. All rights reserved.