From 8efea9d7806b47503a529ac79b6e59cdc5f9214e Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Tue, 16 Jul 2024 14:46:16 +0200 Subject: [PATCH] improve session logging (#712) --- .../MyVaadinSessionInitListener.java | 59 ++++++++++++------- .../exceptionhandling/UiExceptionHandler.java | 17 +++++- .../src/main/resources/application.properties | 10 +++- 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/MyVaadinSessionInitListener.java b/user-interface/src/main/java/life/qbic/datamanager/MyVaadinSessionInitListener.java index b44cbcd38..c86058620 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/MyVaadinSessionInitListener.java +++ b/user-interface/src/main/java/life/qbic/datamanager/MyVaadinSessionInitListener.java @@ -6,11 +6,13 @@ import com.vaadin.flow.component.UI; import com.vaadin.flow.component.page.Page.ExtendedClientDetailsReceiver; import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.server.ServiceDestroyEvent; import com.vaadin.flow.server.ServiceInitEvent; import com.vaadin.flow.server.SessionDestroyEvent; -import com.vaadin.flow.server.SessionDestroyListener; -import com.vaadin.flow.server.VaadinService; +import com.vaadin.flow.server.SessionInitEvent; +import com.vaadin.flow.server.UIInitEvent; import com.vaadin.flow.server.VaadinServiceInitListener; +import com.vaadin.flow.server.WrappedSession; import com.vaadin.flow.spring.annotation.SpringComponent; import life.qbic.datamanager.exceptionhandling.UiExceptionHandler; import life.qbic.datamanager.security.LogoutService; @@ -28,8 +30,7 @@ * Adds listeners to vaadin sessions and ui initialization. */ @SpringComponent -public class MyVaadinSessionInitListener implements VaadinServiceInitListener, - SessionDestroyListener { +public class MyVaadinSessionInitListener implements VaadinServiceInitListener { private static final Logger log = logger(MyVaadinSessionInitListener.class); private final ExtendedClientDetailsReceiver clientDetailsReceiver; @@ -40,7 +41,7 @@ public class MyVaadinSessionInitListener implements VaadinServiceInitListener, public MyVaadinSessionInitListener( @Autowired ExtendedClientDetailsReceiver clientDetailsProvider, @Autowired UiExceptionHandler uiExceptionHandler, - LogoutService logoutService) { + @Autowired LogoutService logoutService) { this.clientDetailsReceiver = clientDetailsProvider; this.uiExceptionHandler = uiExceptionHandler; this.logoutService = logoutService; @@ -48,28 +49,42 @@ public MyVaadinSessionInitListener( @Override public void serviceInit(ServiceInitEvent event) { - event.getSource().addSessionInitListener( - initEvent -> log.debug("A new Session has been initialized! Session " + initEvent.getSession().getSession().getId())); - - event.getSource().addSessionDestroyListener(this); + event.getSource().addSessionInitListener(MyVaadinSessionInitListener::onSessionInit); + event.getSource().addServiceDestroyListener(MyVaadinSessionInitListener::onServiceDestroyed); + event.getSource().addSessionDestroyListener(MyVaadinSessionInitListener::onSessionDestroy); + event.getSource().addUIInitListener(this::onUiInit); + } - event.getSource().addUIInitListener( - initEvent -> { - log.debug("A new UI has been initialized! Session is " + initEvent.getUI().getSession().getSession().getId()); - UI ui = initEvent.getUI(); - ui.getPage().retrieveExtendedClientDetails(clientDetailsReceiver); - ui.getSession().setErrorHandler(errorEvent -> uiExceptionHandler.error(errorEvent, ui)); - ui.addBeforeEnterListener(this::ensureCompleteOidcRegistration); - }); + private void onUiInit(UIInitEvent initEvent) { + log.debug("A new UI has been initialized! ui[%s] vaadin[%s] http[%s] ".formatted( + initEvent.getUI().getUIId(), initEvent.getUI().getSession().getPushId(), + initEvent.getUI().getSession().getSession().getId())); + UI ui = initEvent.getUI(); + ui.getPage().retrieveExtendedClientDetails(clientDetailsReceiver); + ui.getSession().setErrorHandler(errorEvent -> uiExceptionHandler.error(errorEvent, ui)); + ui.addBeforeEnterListener(this::ensureCompleteOidcRegistration); + } + private static void onSessionInit(SessionInitEvent initEvent) { + log.debug("A new Session has been initialized! vaadin[%s] http[%s] ".formatted( + initEvent.getSession().getPushId(), initEvent.getSession().getSession().getId())); + } + private static void onServiceDestroyed(ServiceDestroyEvent serviceDestroyEvent) { + log.debug("Destroying vaadin service [%s]".formatted(serviceDestroyEvent.getSource())); } - @Override - public void sessionDestroy(SessionDestroyEvent event) { - log.debug("Session destroyed."); - event.getSession().getSession().invalidate(); - log.debug("HTTP Session has been invalidated. Id is " + event.getSession().getSession().getId()); + public static void onSessionDestroy(SessionDestroyEvent event) { + WrappedSession wrappedSession = event.getSession().getSession(); + if (wrappedSession != null) { + wrappedSession.invalidate(); + log.debug("Invalidated HTTP session " + wrappedSession.getId()); + } else { + log.debug("Vaadin session [%s] does not wrap any HTTP session.".formatted( + event.getSession().getPushId())); + } + log.debug("Vaadin session destroyed [%s].".formatted(event.getSession().getPushId())); + } private void ensureCompleteOidcRegistration(BeforeEnterEvent it) { diff --git a/user-interface/src/main/java/life/qbic/datamanager/exceptionhandling/UiExceptionHandler.java b/user-interface/src/main/java/life/qbic/datamanager/exceptionhandling/UiExceptionHandler.java index eada0c35c..95dcb588b 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/exceptionhandling/UiExceptionHandler.java +++ b/user-interface/src/main/java/life/qbic/datamanager/exceptionhandling/UiExceptionHandler.java @@ -51,9 +51,20 @@ public void error(ErrorEvent errorEvent, UI ui) { } private void displayUserFriendlyMessage(UI ui, ApplicationException exception) { - requireNonNull(ui); - requireNonNull(exception); - + requireNonNull(ui, "ui must not be null"); + requireNonNull(exception, "exception must not be null"); + if (ui.isClosing()) { + log.error( + "tried to show message on closing UI ui[%s] vaadin[%s] http[%s]".formatted(ui.getUIId(), + ui.getSession().getPushId(), ui.getSession().getSession().getId())); + return; + } + if (!ui.isAttached()) { + log.error( + "tried to show message on detached UI ui[%s] vaadin[%s] http[%s]".formatted(ui.getUIId(), + ui.getSession().getPushId(), ui.getSession().getSession().getId())); + return; + } UserFriendlyErrorMessage errorMessage = userMessageService.translate(exception, ui.getLocale()); ui.access(() -> showErrorDialog(errorMessage)); } diff --git a/user-interface/src/main/resources/application.properties b/user-interface/src/main/resources/application.properties index 6ef2bd6eb..e2faa112e 100644 --- a/user-interface/src/main/resources/application.properties +++ b/user-interface/src/main/resources/application.properties @@ -125,12 +125,18 @@ qbic.broadcasting.identity.topic=User spring.servlet.multipart.max-file-size=16777216 spring.servlet.multipart.fileSizeThreshold=16MB spring.servlet.multipart.max-request-size=16MB -################# Session servlet handling ############################################ +############################################################################### +################# Session servlet handling #################################### +#https://mvysny.github.io/vaadin-session-timeout/ +# Close sessions after the servlet timeout except when there is activity. +# If there is activity and the session timeout is reached, the session is closed after three missed heartbeats. +# Keep in mind that it can take up to 1 minute after timeout to close the session. +# Session closes at server.servlet.session.timeout + 3*vaadin.heartbeatInterval + 0-1min server.servlet.session.timeout=${SESSION_TIMEOUT:30m} vaadin.closeIdleSessions=${SESSION_CLOSEIDLESESSIONS:true} # Heartbeat value is specified as integer time in seconds vaadin.heartbeatInterval=${SESSION_HEARTBEATINTERVAL:60} -################# Session cookie handling ############################################ +################# Session cookie handling ##################################### server.servlet.session.cookie.max-age=${SESSION_COOKIE_MAX_AGE:30m} server.servlet.session.cookie.secure=${SESSION_COOKIE_SECURE:true} server.servlet.session.cookie.domain=${SESSION_COOKIE_DOMAIN:de.uni-tuebingen.qbic}