Heartbeat not working - 7.3.8

I’ve a problem with heartbeat.
I created a DetachListener (only System.out.println) and added it to my UI.
Then I started the application and closed the browser.
My heratbeatInterval is 100 seconds so I waited a DetachEvent after 300 or 400 secs.
Nothing appened until the session timeout (30 minutes).
Should I start heartbeat in someway ?
Did I miss something ?
Tks

I find that the second and subsequent UIs associated with a session will time out according to the heartbeat and trigger the detach listener. The first UI (or perhaps better stated, the last remaining UI in a session) will persist until the session timeout. I don’t know if that’s the intended behaviour, but that’s what I get with my app. I would prefer if all UIs would time out under the control of the heartbeat. I use the latest Vaadin, Tomcat, Java 8, servlet 3.0, manual push using websocket.

It’s quite dramatic because I use a single UI and that mean I’ve no heartbeat in my application.
Could someone suggest a solution or a workaround ?
Tks

Hi,

Heartbeat is a “best-effort” mechanism mostly used to prevent serious memory/resource leaks caused by UIs piling up in a session. There is no cleanup thread running in the background disposing of idle UIs; instead old UI instances are cleaned up on each request within the same session. If in your use case there is usually only a single UI per session and you want to release some resources quicker, you should simply configure a shorter session timeout.

I believe there are two different needs :
. I need to be informed as soon as possible if the user leaved the application abbruptly (I mean after 2/3 minutes) to cleanup resources
. I need to cleanup a session idle from a long time.

I can’t use the same value (session timeout) for both situations.
Usually heartbeat system will solve the first issue and session timeout the second one.
How can I solve ?
Tks

There’s no solution ?
Any further help ?
Tks

You can setup your own cleanup thread or a java.util.Timer that periodically checks whether the UI is still active (you can use UI.getLastHeartbeatTimestamp() for this), and removes the UI from the session or at least disposes of any custom resources it has open, something like:

public class MyUI extends UI {

    // Safe to access concurrently
    private static final Timer uiCleanupTimer = new Timer(true);

    @Override
    public void init(VaadinRequest request) {

        final int cleanupIntervalMillis =
                3 * 1000 * getSession().getConfiguration().getHeartbeatInterval();

        uiCleanupTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                final MyUI ui = MyUI.this;
                ui.access(new Runnable() {
                    @Override
                    public void run() {
                        if (System.currentTimeMillis() >
                                ui.getLastHeartbeatTimestamp() + cleanupIntervalMillis) {
                            // this check should really be internal in removeUI :(
                            if (!ui.isClosing()) {
                                ui.close();
                            }
                            ui.getSession().removeUI(ui);
                            cancel();
                        }
                    }
                });
            }
        }, cleanupIntervalMillis, cleanupIntervalMillis);
    }
}

Or you could write a per-session task that loops through all the UIs in the session.

In general, Vaadin cannot implement such a cleanup thread as we’re not in control of the concurrency environment. For instance, in a JEE environment spawning custom threads is very frowned upon, and an ExecutorService should be used instead.

Hi Johannes. Thanks very much for that code example, it’s going to help me out a lot here.

Brief background:
I have a Vaadin application (currently 7.3.5) with a new requirement. I have to pull a license key on login, and release it when appropriate. More specifically, “when appropriate” would be on logout, or browser close, or tab close, or address bar refresh.

With that in mind, I took your nested example and put it in separate classes.

Main UI additions to DesigntrackerUI class (which now implements DetachListener)

private static Timer uiCleanupTimer = null; 
@Override
    protected void init(VaadinRequest request) {
...
final int cleanupIntervalMillis = 3 * 1000 * heartbeatInterval;
DesignTrackerShutdownTimerTask task = new DesignTrackerShutdownTimerTask(this, sessionContainer, cleanupIntervalMillis);
uiCleanupTimer = new Timer(true);
uiCleanupTimer.schedule(task, cleanupIntervalMillis, cleanupIntervalMillis);
...
}
...
@Override
    public void detach(DetachEvent event) {
        // tell the server so we can release the license
        String userName = sessionContainer.getUserName();
        logger.info("===== CLIENT SHUTDOWN for "+userName+" =====");
        DesigntrackerUI src = (DesigntrackerUI)event.getSource();
        // do whatever is needed to communicate the license release, then...
        //uiCleanupTimer.cancel(); // Trying to prevent the recurring UIDetachedException messages, 
        //uiCleanupTimer = null;   // but it is not working.
    }

The DesignTrackerShutdownTimerTask class:

public class DesignTrackerShutdownTimerTask extends TimerTask implements Serializable {
    private static final long serialVersionUID = 1L;
    final static Logger logger = LoggerFactory.getLogger(DesignTrackerShutdownTimerTask.class);
    DesigntrackerUI ui = null;
    SessionContainer sessionContainer = null;
    int cleanupIntervalMillis = 1000; // gets reset anyway
    public DesignTrackerShutdownTimerTask(DesigntrackerUI ui, SessionContainer sessionContainer, int cleanupIntervalMillis){
        this.ui = ui;
        this.sessionContainer = sessionContainer;
        this.cleanupIntervalMillis = cleanupIntervalMillis;
    }
    @Override
    public void run() {
        DesignTrackerShutdownRunnable runnable = new DesignTrackerShutdownRunnable(ui, this, cleanupIntervalMillis);
        try{
            //if (ui.isAttached()){ // this avoids the UIDetachedException, but I shouldn't be here in the first place
                ui.access(runnable);
            //}
        }catch (com.vaadin.ui.UIDetachedException e){
            logger.info("UIDetachedException detected, ui class="+ui);
            String userName = sessionContainer.getUserName();
            logger.error("UIDetachedException found when handling detach for user="+userName);
            logger.error("detached exception is "+e.getLocalizedMessage());
        }
    }
}

The DesignTrackerShutdownRunnable class:

public class DesignTrackerShutdownRunnable implements Runnable {
    final static Logger logger = LoggerFactory.getLogger(DesignTrackerShutdownRunnable.class);
    DesigntrackerUI ui = null;
    DesignTrackerShutdownTimerTask task = null;
    int cleanupIntervalMillis = 1000; // gets reset anyway
    DesignTrackerShutdownRunnable (DesigntrackerUI ui, DesignTrackerShutdownTimerTask task, int cleanupIntervalMillis){
        this.ui = ui;
        this.task = task;
        this.cleanupIntervalMillis = cleanupIntervalMillis;
    }
    @Override
    public void run() {
        if (System.currentTimeMillis() >
                ui.getLastHeartbeatTimestamp() + cleanupIntervalMillis) {
            if (!ui.isAttached()){
                logger.warn("This ui is already unattached, so problems will follow");
            }
            // this check should really be internal in removeUI :(
            if (!ui.isClosing()) {
                ui.close();
            }
            ui.getSession().removeUI(ui);
            logger.info("calling cancel on the task");
            task.cancel();
        }
    }

}

VaadinServlet additions in web.xml:

<!-- Heartbeat control to detect closed browsers -->
        <init-param>
            <param-name>heartbeatInterval</param-name>
            <param-value>5</param-value>
        </init-param>
        <!-- This one is not needed to make shutdown work. -->
        <init-param>
            <param-name>closeIdleSessions</param-name>
            <param-value>true</param-value>
        </init-param>

With heartbeatInterval set to 5 seconds, cleanupIntervalMillis will be set at 15 seconds
The code almost works like I expect. When I refresh the browser window, the detach gets triggered, but the old ui class is hanging around and throws the UIDetachedException.

after starting the application, I enter the address in the browser window and get ui instance logged (481115fe):

16:22:18.756 [http-bio-8888-exec-3]
 INFO  c.s.l.g.d.DesigntrackerUI - heartbeat interval (seconds): 5
16:22:18.760 [http-bio-8888-exec-3]
 INFO  c.s.l.g.d.DesigntrackerUI - No polling interval set in lynxweb.conf, so using server push mode.
16:22:18.760 [http-bio-8888-exec-3]
 INFO  c.s.l.g.d.DesigntrackerUI - GUI being initialized on a web browser, ui class=...DesigntrackerUI@481115fe

then I refresh the browser address bar, getting the new ui instance (546f29da)…

16:22:29.223 [http-bio-8888-exec-3]
 INFO  c.s.l.g.d.DesigntrackerUI - heartbeat interval (seconds): 5
16:22:29.223 [http-bio-8888-exec-3]
 INFO  c.s.l.g.d.DesigntrackerUI - No polling interval set in lynxweb.conf, so using server push mode.
16:22:29.223 [http-bio-8888-exec-3]
 INFO  c.s.l.g.d.DesigntrackerUI - GUI being initialized on a web browser, ui class=...DesigntrackerUI@546f29da

And then here comes the detach, as expected, then the exceptions against the old ui instance (481115fe).

16:22:29.282 [http-bio-8888-exec-3]
 INFO  c.s.l.g.d.DesigntrackerUI - ===== CLIENT SHUTDOWN for null =====
16:22:33.761 [Timer-0]
 INFO  c.s.l.g.d.DesignTrackerShutdownTimerTask - UIDetachedException detected, ui class=...DesigntrackerUI@481115fe
16:22:33.761 [Timer-0]
 ERROR c.s.l.g.d.DesignTrackerShutdownTimerTask - UIDetachedException found when handling detach for user=null
16:22:33.761 [Timer-0]
 ERROR c.s.l.g.d.DesignTrackerShutdownTimerTask - detached exception is null
16:22:48.762 [Timer-0]
 INFO  c.s.l.g.d.DesignTrackerShutdownTimerTask - UIDetachedException detected, ui class=...DesigntrackerUI@481115fe
16:22:48.762 [Timer-0]
 ERROR c.s.l.g.d.DesignTrackerShutdownTimerTask - UIDetachedException found when handling detach for user=null
16:22:48.763 [Timer-0]
 ERROR c.s.l.g.d.DesignTrackerShutdownTimerTask - detached exception is null
16:23:03.764 [Timer-0]
 INFO  c.s.l.g.d.DesignTrackerShutdownTimerTask - UIDetachedException detected, ui class=...DesigntrackerUI@481115fe
16:23:03.764 [Timer-0]
 ERROR c.s.l.g.d.DesignTrackerShutdownTimerTask - UIDetachedException found when handling detach for user=null
16:23:03.764 [Timer-0]
 ERROR c.s.l.g.d.DesignTrackerShutdownTimerTask - detached exception is null
16:23:18.765 [Timer-0]
 INFO  c.s.l.g.d.DesignTrackerShutdownTimerTask - UIDetachedException detected, ui class=...DesigntrackerUI@481115fe
16:23:18.765 [Timer-0]
 ERROR c.s.l.g.d.DesignTrackerShutdownTimerTask - UIDetachedException found when handling detach for user=null
16:23:18.766 [Timer-0]
 ERROR c.s.l.g.d.DesignTrackerShutdownTimerTask - detached exception is null

All I really understand is that the exceptions are coming every 15 seconds because of the 5 second timeout and the multiplication by 3 in the main ui. The ===== CLIENT SHUTDOWN for null ===== is null because I did not bother logging in. Once I get the lifecycle of this timer task and ui figured out, I can go do the real work.

From reading other threads on UIDetachedException, I am led to understand that this is caused by the ui class holding resources it should release. I thought the closeIdleSessions setting in web.xml would help close the ui, but it didn’t. I don’t really know what I am looking for, since I can’t understand what resources would hold the ui open like that.

Is there something I should be doing in the detach? As you can see, I tried managing the uiCleanupTimer object (removing tasks, nulling) but that did not work.

I can guard the exception by checking isAttached(), but that just masks the problem.

Is this caused by a ui class holding resources? What resources should I be looking for?

Someone please help me out here…

After working with this some more, the problem only comes up when I refresh the address bar. Killing the tab or the entire browser window behaves normally.