Skip to main content

@PreDestroy on a ViewScoped managed bean

12 replies [Last post]
judys
Offline
Joined: 2009-07-23
Points: 0

I have a managed bean that needs to remove its data listener from an ApplicationScoped data model, and I would like to put the managed bean in View scope. So I need to know when its lifespan is up.

I naively thought that ViewScoped managed beans expire when I change to a different page in the browser, but I see that it's not so simple. If I then use the browser's back (or forward) button the view is still there, maintaining its state. I was also a little surprised to see that the ViewScoped bean survives destroying the session.

Anyway, I put a method with a @PreDestroy annotation on the ViewScoped bean, and also registered a ComponentSystemEventListener on the view root for PreDestroyViewMap events.

In the server logs, I see it create new instances of my bean when I reload the page or follow a link to it, but I have never seen either the PreDestroy method or the listener called. (The PostConstruct method DOES get called).

Am I confused, is there a bug, or do they just live forever? Or is there another way to find out when they expire? Is it possible to have more than one UIViewRoot on the same page concurrently in one session? What triggers their expiration? I haven't found much detail on this, so if anyone knows of any doc, please let me know.

Here's my code for registering the listener. I tried calling it in the bean's constructor and in the PostConstruct method, which does get called.

private void registerViewListener() {

FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot viewRoot = context.getViewRoot();
viewRoot.subscribeToEvent(PreDestroyViewMapEvent.class, new ViewMapExitHandler());
}

public class ViewMapExitHandler
implements ComponentSystemEventListener, Serializable {

public ViewMapExitHandler() {}

public void processEvent (ComponentSystemEvent event) {

System.out.println("ViewMapExitHandler");
diagram.setDataModel(null);
event.getComponent().unsubscribeFromEvent(PreDestroyViewMapEvent.class, this);
}
}

Thanks for any help.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
driscoll
Offline
Joined: 2003-06-06
Points: 0

The following code is working fine for me:

import java.io.Serializable;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean
@ViewScoped
public class ViewWatcher implements Serializable {

long serialVersionUID = 0L;
volatile Logger log = Logger.getLogger("viewwatcher");
String hello = "Hello";

@PostConstruct
public void init() {
log.severe("starting view");
}

@PreDestroy
public void finish() {
log.severe("finishing view");
}

}

With a series of pages that include #{viewWatcher.hello}

So - what ever is going on in your code, @PreDestroy is being called at the right time.

judys
Offline
Joined: 2009-07-23
Points: 0

How do you do it?

I have just downloaded and installed Java 1.6u16 and glassfish v3 web preview, applied its one update, and copied in the oct 7 jsf jars.

My test bean is identical to yours except for the class name. I have two xhtml pages and a build environment as vanilla as possible. My test pages reference the bean and have links to each other. I even increment a static counter in case the log message gets lost for some reason.

I am building with 1.6u16 against glassfish v3's jars and deploying on glassfish v3 on Mandriva 2007 (which admittedly is not the latest).

When I click the links in the pages I see "starting view" over and over and over, but never "finishing view", and the counter is not incremented.

Can you send me your faces-config and web.xml and one of your xhtml pages in case I have some difference there?

And just to keep priorities straight, this is a "would be nice" not a "must have", I can always use a SessionScoped bean with a session listener instead.

driscoll
Offline
Joined: 2003-06-06
Points: 0

I've attached a working Netbeans project to the bug, at:

https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=1351

Let's take this discussion there.

judys
Offline
Joined: 2009-07-23
Points: 0

I've applied to be an Observer but as yet I can't post to the issue.

Thx for the project code, I see now what's happening.

If you exit the page with an

I think in my case I would run the risk of accumulating dead diagrams which can't be displayed and can't be garbage collected because the data model still has refs to their listeners, and that would be a problem.

driscoll
Offline
Joined: 2003-06-06
Points: 0

Thanks Judy, I'll bring this up to the folks who really understand this code section.

driscoll
Offline
Joined: 2003-06-06
Points: 0

So other folks can tell what the resolution was, without going to the bug database, here's what I wrote in the bug:

--------------

I talked with Ryan and Ed about this, and here's what they said, paraphrased.

Essentially, this is expected behavior.

Imagine a case where you have multiple tabs open, or press the back button - in
both cases, under the case where you're doing a GET, you'd expect the state to
be there - otherwise, you'll get a ViewExpiredException.

This means that if you also do this will also be the behavior.

The views will all be reaped when the session expires, of course, which is often
set to 30 or 60 minutes.

There's also a configurable limit to the number of views - 15 is the default,
and it's a LRU (Least Recently Used) queue.

judys
Offline
Joined: 2009-07-23
Points: 0

That's fair, and I realize it's not such a simple thing. I hope the book authors are paying attention, because if you read what's out there today you can get a different impression.

To follow up on the other PreDestroy issue, here are some observations. I realize starting and stopping the server is kind of artificial, but I don't know how else to make it serialize the session.

1. PreDestroy on a SessionScoped bean always seems to get called twice.

2. If you shut down the web server, it serializes the SessionScoped bean and then calls its PreDestroy. When you restart the server, it deserializes the bean and you can continue with the page, but the bean has already had PreDestroy called. Then when you start a new session, PreDestroy is not called on the bean. (as it would be for an instantiated bean.)

3. If you shut down the web server, it serializes the ViewScoped bean in the current view. When you restart the server, it deserializes the bean and you can continue with the page, but when you go to a new page (with h:commandLink) PreDestroy is never called on the bean (as it would be for an instantiated bean.)

4. You can no longer use a SessionScoped bean as an HttpSessionBindingListener to find out when the session ends because it gets added to and removed from the session many times (but of course you can use an anonymous or other class that is not a managed bean).

Again, these are not issues for me, I just wrote them up here in case they are bugs, or anyone else needs to know.

armen2010
Offline
Joined: 2008-05-08
Points: 0

As i Understand @PostConstruct and @Pre Destroy do not correctly work under tomcat, they good work under application servers like jboss or glassfish. Under jsf 2.0 work only @PostConstruct. I read book of David Geary, Cay Horstmann, Core Java Server faces 2007, and found
"In particular, Java EE 5-compliant application servers such as GlassFish support these annotations. It is expected that standalone containers such as Tomcat will also provide support in the future." Too bad... because at the moment tomcat do not fully support its.

judys
Offline
Joined: 2009-07-23
Points: 0

Yes, thank you. I have been working at the servlet level lately and I'm noticing that session termination events at shutdown don't seem to get sent in Tomcat as they do in Glassfish.

driscoll
Offline
Joined: 2003-06-06
Points: 0

Well, according to the spec:

JSF.5.4.1

In the case of a managed bean placed in view scope, methods annotated
with @PreDestroy must only be called when the view scope is destroyed.
See the javadoc for FacesContext.setViewRoot().

So if you're not seeing that behavior, then that's probably a bug.

I've taken the liberty of filing a bug on this so the Mojarra team can check it out (bug 1351).

judys
Offline
Joined: 2009-07-23
Points: 0

Thanks. What I'm not clear on is when the view scope is destroyed.

On a related topic, I made the managed bean a SessionBean and by stopping and restarting Tomcat, I watched it serialize and deserialize the bean instance with the session. It seems that PreDestroy doesn't get called on instances that were deserialized when Tomcat was restarted. If you create a new one in a new session, PreDestroy does get called as expected.

As always I'm happy to produce test cases for both of these issues.

driscoll
Offline
Joined: 2003-06-06
Points: 0

The view scope map is destroyed when something calls FacesContext.setViewRoot(root).

This will usually happen in the invoke application phase.

At that time, the properties map of the view is cleared, which is what causes @PreDestroy to be triggered. When the actual UIViewRoot object is destroyed turns out not to be important to this discussion (it's garbage collected at some point later).

(Thanks to Ed Burns for patiently explaining this to me.)