Vaadin Spring Boot: невозможно получить доступ к состоянию в VaadinSession или пользовательском интерфейсе без блокировки сеанса, когда несколько пользователей обращаются к пользовательскому интерфейсу

У меня есть приложение vaadin flow (13) с пружинным ботинком. У меня есть VerticalLayout, помеченный @UIScope с несколькими вложенными макетами. Дополнительные макеты помечаются @Scope("prototype") и создаются с помощью ctx.getBean(SubUI.class, ...). В зависимости от выбора и нажатия кнопок в родительском пользовательском интерфейсе (VerticalLayout) добавляются или удаляются вложенные макеты.

Все это работает нормально, пока я использую только приложение (именно поэтому я обнаружил ошибку прямо сейчас, после доставки приложения моему клиенту для проверки acceptantce). Они тестировали с несколькими пользователями. Пока один пользователь работает с приложением, все работает, но как только второй пользователь входит в пользовательский интерфейс (VerticalLayout), возникает следующее исключение:

java.util.concurrent.ExecutionException: java.lang.IllegalStateException: Cannot access state in VaadinSession or UI without locking the session.
    at java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[na:1.8.0_202]
    at java.util.concurrent.FutureTask.get(FutureTask.java:192) ~[na:1.8.0_202]
    ...

Я пробовал разные аннотации для своего родительского макета (@UIScope, @Scope ("прототип"), @VaadinSessionScope), но это не решило мою ошибку.

Я погуглил о проблеме и нашел несколько тем, но ни один из них не помог мне решить мою проблему. Я не совсем уверен, вызвана ли проблема ограничением объема моих пользовательских интерфейсов / макетов или, возможно, привязкой данных. В моих дополнительных макетах данные связаны с использованием Binder<MyEntity> binder = new Binder<>(MyEntity.class); и binder.forField(xxx).bind(MyEntity::getXY,MyEntity::setXY);.

Может кто-нибудь объяснить мне, в чем моя проблема? Я был бы очень счастлив, потому что мне нужно поставить фиксированную версию для тестирования моих клиентов.

С уважением


person tigu    schedule 12.02.2020    source источник
comment
Не могли бы вы поделиться полной трассировкой стека, а также попробовать запустить приложение с включенными утверждениями (-ea в качестве параметра JVM), чтобы узнать, дает ли это дополнительную информацию?   -  person Leif Åstrand    schedule 12.02.2020


Ответы (1)


В вашем приложении есть логика, которая пытается изменить состояние пользовательского интерфейса, компоненты и т. Д. Из фонового потока. Это должно быть сделано с замком. Итак, вам нужно инкапсулировать логику в вашем представлении, которая вызывается из фонового метода с помощью

getUI().ifPresent(ui -> ui.access(() -> {
    ...
}));
person Tatu Lund    schedule 12.02.2020
comment
Спасибо! Так что именно мне нужно инкапсулировать? Процедуры, в которых изменен пользовательский интерфейс? У меня есть процедура под названием editDataBasedOnSelection(), которая добавляет необходимые вспомогательные представления. Эта процедура вызывается в кнопке ClickListener. Так правильно ли инкапсулировать вызов процедуры внутри прослушивателя кликов, например myButton.addClickListener(event -> {<your encapsulation code here>editDataBasedOnSelection()<end encapsulation>});? - person tigu; 12.02.2020
comment
И, может быть, у вас есть ссылка на документацию, в которой подробно объясняются предыстория и вся концепция? Мне бы очень хотелось лучше понять, что я здесь делаю. В любом случае, я постараюсь заставить его работать с твоей подсказкой сегодня вечером. - person tigu; 12.02.2020
comment
@tigu внутри clicklistener вам не нужно инкапсулировать изменения в пользовательском интерфейсе. Это нужно делать только тогда, когда вы вручную запускаете свой Thread. Каждая модификация пользовательского интерфейса, которую вы делаете из этого фонового потока, должна быть инкапсулирована. См. Также: Асинхронные обновления. Вы начинаете свою собственную тему где-нибудь в вашем поле зрения? - person kscherrer; 12.02.2020
comment
@kscherrer, нет, я не создаю собственных тем. Но с подсказками от вас и @Tata ​​Lund сегодня я снова начал его отлаживать и нашел решение. Я добавил / удалил компонент с автоматическим подключением, который был помечен @UIScope. Я изменил его на @Scope("prototype") и загрузил с помощью ctx.getBean(). Теперь мой интерфейс работает даже с несколькими пользователями. - person tigu; 13.02.2020