Cleaning Resources fails

Hello,

I created a Countdown component. This countdown starts a timer.
My code works (progressbar is updated accordingly, just removed some code fragments to show you a cleaner code):

[code]
private void startTimer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {

        @Override
        public void run() {
            int secondsLeft = Seconds.secondsBetween(new DateTime(new Date()), endDate).getSeconds();
            double percent = (100 - ((double) secondsLeft * 100 / (double) startEnd)) / 100;

            if (secondsLeft <= 0) {
                timer.cancel();
                synchronized (UI.getCurrent()) {
                    bar.setValue(1f);
                }
                UI.getCurrent().setPollInterval(-1);
            }
            else {
                synchronized (UI.getCurrent()) {
                    bar.setValue((float) percent);
                }
            }
        }
    }, 0, 1000);
    UI.getCurrent().setPollInterval(500);
}

[/code]My problem: The timer runs in the background despite the fact that the user leaves the page.
I started the timer with a debug message and closed my browser. The timer is still running.

I added a detach listener to the current UI to cancel the timer:

[code]

 UI.getCurrent().addDetachListener(new DetachListener() {

            @Override
            public void detach(DetachEvent event) {
                timer.cancel();
            }
        });

[/code]But that doesnt work either.

Then I tried to change the heartbeat interval. Since Vaadin cleans the UI if the client sends no heartbeat I thought thats the solution. So I set the heartbeat to 5 seconds:

[code]
@VaadinServletConfiguration(…, heartbeatInterval = 5)

[/code]I started the timer once again and closed my browser. Waited about 1 minute… But the timer does not get cancelled its still running in the background.
The detach listener is executed ONLY when I reenter the site.

Can you help me to fix my problem? :frowning:

Best regards

Hi,

if I remember correctly, the UI cleanup on vaadin is done only when there is other activity (requests) in the server. In practice this means that one or more UIs may remain in memory until the next request comes in. Usually this is not an issue if the UI/Session footprint is not that large
and
there is periodic traffic to the application.

In some projects a dedicated cleanup thread has been implemented to periodically clean up expired UIs. Unfortunately I don’t remember how exactly it was done :frowning: Maybe someone else can chime in?

-tepi

Hello,

yeah… thats really bad :frowning:

I solved my issue by checking whether there is a connected pushconnection. If not,
the timer is cancelled.

For those who are interested in a progress bar that displays the remaining time,
you can take a look at my full example.

public class TimeBar extends CssLayout {


    private DateTime startDate;
    private DateTime endDate;
    private int secondsBetweenStartEnd;
    private ProgressBar bar;
    private Label time;
    private Timer timer;


    public TimeBar(Date startDate, Date endDate) {
        super();
        this.startDate = new DateTime(startDate);
        this.endDate = new DateTime(endDate);
        this.secondsBetweenStartEnd = Seconds.secondsBetween(this.startDate, this.endDate).getSeconds();
        buildBar();
        startTimer();
        UI.getCurrent().addDetachListener(new DetachListener() {

            private static final long serialVersionUID = 1L;


            @Override
            public void detach(DetachEvent event) {
                timer.cancel();
            }
        });
    }


    private void startTimer() {
        timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                UI.getCurrent().access(new Runnable() {

                    @Override
                    public void run() {
                        if (UI.getCurrent().getPushConnection().isConnected()) {
                            int secondsLeft = Seconds.secondsBetween(new DateTime(new Date()), endDate).getSeconds();
                            double percent = (100 - ((double) secondsLeft * 100 / (double) secondsBetweenStartEnd)) / 100;

                            if (secondsLeft <= 0) {
                                timer.cancel();
                                bar.setValue(1f);
                                time.setCaption("No time left.");
                                // addComponentAsFirst(AudioUtils.getAudio(AudioFile.TIMEUP, true));
                            }
                            else {
                                bar.setValue((float) percent);
                                time.setCaption("Remaining time: " + TimeUtils.millisToSimpleFormat(secondsLeft * 1000));
                            }
                        }
                        else {
                            timer.cancel();
                        }
                    }
                });

            }
        }, 0, 1000);
    }


    private void buildBar() {
        bar = new ProgressBar();
        bar.setStyleName("verticalAlignMiddle");
        time = new Label();
        addComponent(bar);
        addComponent(time);
        bar.setWidth("300px");
    }


    public void stop() {
        if (timer != null) {
            timer.cancel();
        }
    }

}