Vaadin 14 - How to make sure of session related objects get cleaned up after SessionDestroy

Majid :

Here is my MainView Class in a Vaadin 14.1.18 SpringBoot application.

@Route
public class MainView extends VerticalLayout implements SessionDestroyListener{
    Logger logger = LoggerFactory.getLogger(MainView.class);

    public MainView() {
        // To make sure of doing some Houskeeping after Session Timeout
        VaadinService.getCurrent().addSessionDestroyListener(this);

        String sessionId = VaadinSession.getCurrent().getSession().getId();
        Zombie zombie = new Zombie(sessionId);
        zombie.start();

        add(new Span("Hey There! I'm at your disposal!!"));
    }

    @Override
    public void sessionDestroy(SessionDestroyEvent sessionDestroyEvent) {
        logger.warn("Just received a SessionDestroy Event with SessionId :: [{}]", sessionDestroyEvent.getSession().getSession().getId());
        // Performing the HouseKeeping stuff
    }
}

I've also implemented a Zombie process as following which I had instantiated in my MainView Class.

public class Zombie extends Thread {
    Logger logger = LoggerFactory.getLogger(Zombie.class);
    private String sessionId;

    public Zombie(String sessionId) {
        this.sessionId = sessionId;
    }

    public void run() {
        while (true) {
            try {
                Thread.sleep(60000);
                Calendar cal1 = Calendar.getInstance();
                logger.info("Session [{}] :: Hey, I'm still alive at {}", sessionId, cal1.getTime());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

The question is, When the respective session gets destroyed, why the objects attached to it remain? the following log shows that, even after SessionDestroy, the zombie process is still running. Do I have to take some specific measures to explicitly clean these objects or Vaadin should handle it automatically somehow?

2020-03-07 02:32:02.891  INFO 6336 --- com.mypackage.Zombie : Session [B2CBB897208717EAE264C739E2D565BE] :: Hey, I'm still alive at Sat Mar 07 02:32:02 IRST 2020  
2020-03-07 02:32:56.580  WARN 6336 --- com.mypackage.MainView : Just received a SessionDestroy Event with SessionId :: [B2CBB897208717EAE264C739E2D565BE]  
2020-03-07 02:33:02.891  INFO 6336 --- com.mypackage.Zombie : Session [B2CBB897208717EAE264C739E2D565BE] :: Hey, I'm still alive at Sat Mar 07 02:33:02 IRST 2020  
2020-03-07 02:34:02.892  INFO 6336 --- com.mypackage.Zombie : Session [B2CBB897208717EAE264C739E2D565BE] :: Hey, I'm still alive at Sat Mar 07 02:34:02 IRST 2020
Erik Lumme :

You mentioned garbage collection in another question. In most cases, after a session is destroyed, it will be garbage collected.

When the Java GC runs, it may garbage collect everything that is not accessible through a garbage collection root. Threads are one type of root, and are never garbage collected as long as they are running.

Vaadin can not know what your intentions with threads are, nor how to stop them safely. As such, it is your responsibility to stop them.

There are generally two ways to stop a thread, using interrupt() or with your own flag.

Which one you should use depends on your use case. If your thread is sleeping, or otherwise waiting, an interrupt will interrupt it immediately (as the name implies) and an InterruptedException will be thrown.

If you go with this approach, change your while(true) to while(!Thread.interrupted()), and simply call zombie.interrupt() to stop it. If the thread is currently sleeping, your catch block will be reached, so you might not need to log the exception if it's normal behavior.

Note: Thread.interrupted() resets the interrupted flag after it's called, so you shouldn't, for example, call it in the catch block. You can use isInterrupted() if you want to read the state without resetting it.

The other option is to use your own flag. The simplest approach is to add a private volatile boolean stopped = false, and change your while (true) to while(!stopped).

The volatile keyword ensures the value is not cached in the CPU, which could delay the time it takes for your thread to notice the value change.

You can then add a method to stop it, e.g. public void stop() { this.stopped = true; }, and call that in the session destroy listener. As this approach does not interrupt a waiting thread, it will always finish the current iteration of the loop, including the sleep if it is currently sleeping.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=169921&siteId=1