Cannot access state in VaadinSession or UI without locking the session

When I call refreshAll() on the dataprovider in an clickevent handler on a button to update a listbox I get the above exception. The call is made within an ui.access() like this but it doesn’t help:

	okButton = new Button("OK", clickEvent -> {
		if (commitItemEdit()) {
			ui.access(() -> {
				dataProvider.refreshAll();
			});
        }
	}

I’ve tried also without wrapping in ui.access() but no difference. Is there anything else that should be done to aquire that lock? My understanding was that since it is within the scope of a webcall then it shouldn’t been necessary to do that wrapping?

Version 13.0.0.beta2

The example at https://vaadin.com/docs/v13/flow/binding-data/tutorial-flow-data-provider.html shows dataProvider.refreshAll() being called from a clickevent handler without being within UI.access(), but that also gives the exception in the topic.

Hi Sverker,

Can you provide some more information:

  • Where is the UI instance coming from?
  • Where is the dataProvider instance coming from?

-Petter-

For the obtaining the ui instance I’ve tried two different variant:

	protected UI ui;
	protected void onAttach(final AttachEvent attachEvent) {
		ui = attachEvent.getUI();
	}

and

		okButton = new Button("OK", clickEvent -> {
			if (commitItemEdit()) {
				UI.getCurrent().access(() -> {
					dataProvider.refreshAll();
				});
			}
		});

As mentioned above I’ve also tried without UI.access():

		okButton = new Button("OK", clickEvent -> {
			if (commitItemEdit()) {
				dataProvider.refreshAll();
			}
		});

In https://vaadin.com/docs/v13/flow/binding-data/tutorial-flow-data-provider.html section “Notifying the Data Provider About Item Changes” the refresh is done in the examples in a clickevent handler of a button without UI.access().

The data provider is injected:

	@Inject
	private void setDataProvider(ProviderDataProvider dataProvider) {
		this.dataProvider = dataProvider;
	}

Some other information which could be relevant. I have an abstract base class where the button is created and which has the dataprovider instance. That class is extended by the page classes, which are stateful session beans. I doubt though that it should make any difference, and everything else is working fine. I can follow the stack trace to the right location both with and without the refreshAll() call within UI.access()

As mentioned on the call I have another page which is also a SFSB with a grid where update events are received via jms and refresh method is called with UI.access(), because that happens from a thread which is not part of a ui access. Updates on that page works fine.

What scope does the data provider have? It could be that you have ended up with a data provider that is shared between multiple sessions, which could explain why you get the exception.

Also, did I understand it correctly that you are using SFSBs inside your UI? May I ask why? Also, how are these session beans scoped?

The ui bean was stateful for historical reasons, it doesn’t have to be and it doesn’t solve the issue to remove @Stateful annotation. Dataprovider is in default scope, i.e. dependant, and there is only one session so issue can’t be that it’s shared over several session.

Looking with debugger and breakpoints I see that the thread calling the clickevent handler is named e.g. Task-16 which is also the same thread calling ListBox.rebuild(), but where the VaadinSession.hasLock() is called the thread name is ForkJoinPool.commonPool-worker7. That is why the exception is thrown, that thread does not have a lock. Look at these parts of the stacktrace (very long so I cut what happens before dataprovider.refreshAll())

This part is still the same thread as is calling clickevent handler:

Caused by: java.lang.IllegalStateException: java.lang.IllegalStateException: Cannot access state in VaadinSession or UI without lock
ing the session.
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:593)
        at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677)
        at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:735)
        at java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:160)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:174)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
        at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
        at com.vaadin.flow.component.listbox.ListBox.rebuild(ListBox.java:178) <--- still thread Task-16
        at com.vaadin.flow.component.listbox.ListBox.lambda$setDataProvider$78e8296$1(ListBox.java:94)
        at com.vaadin.flow.data.provider.AbstractDataProvider.lambda$fireEvent$2(AbstractDataProvider.java:96)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
        at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
        at java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1699)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
        at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
        at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
        at com.vaadin.flow.data.provider.AbstractDataProvider.fireEvent(AbstractDataProvider.java:94)
        at com.vaadin.flow.data.provider.AbstractDataProvider.refreshAll(AbstractDataProvider.java:53)

This is from the ForkJoinPool.commonPool-worker7

Caused by: java.lang.IllegalStateException: Cannot access state in VaadinSession or UI without locking the session.
       at com.vaadin.flow.server.VaadinSession.checkHasLock(VaadinSession.java:509)
       at com.vaadin.flow.server.VaadinSession.checkHasLock(VaadinSession.java:523)
       at com.vaadin.flow.internal.StateTree.checkHasLock(StateTree.java:390)
       at com.vaadin.flow.internal.StateTree.markAsDirty(StateTree.java:258)
       at com.vaadin.flow.internal.StateNode.markAsDirty(StateNode.java:510)
       at com.vaadin.flow.internal.nodefeature.NodeList.addChange(NodeList.java:277)
       at com.vaadin.flow.internal.nodefeature.NodeList.add(NodeList.java:238)
       at com.vaadin.flow.internal.nodefeature.StateNodeNodeList.add(StateNodeNodeList.java:52)
       at com.vaadin.flow.internal.nodefeature.ElementChildrenList.add(ElementChildrenList.java:42)
       at com.vaadin.flow.dom.impl.AbstractNodeStateProvider.insertChild(AbstractNodeStateProvider.java:102)
       at com.vaadin.flow.dom.Node.insertChild(Node.java:250)
       at com.vaadin.flow.dom.Node.appendChild(Node.java:141)
       at com.vaadin.flow.component.HasComponents.add(HasComponents.java:53)
       at com.vaadin.flow.component.listbox.ListBox.lambda$rebuild$2(ListBox.java:178) <---- This is where the worker is fetched?
       at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
       at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
       at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
       at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
       at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291)
       at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
       at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
       at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
       at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
       at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

Some more tests, refreshing a single item works fine. It’s when calling ListBox.rebuild() that the exception occurs.

It looks like the stream returned by the DataProvider is parallel. Which implementation of DataProvider are you using?

My dataprovider extends com.vaadin.flow.data.provider.AbstractBackEndDataProvider and indeed it returns a parallel stream:

	protected Stream<T> fetchFromBackEnd(final Query<T, F> query) {
		CriteriaQuery<T> cq = createSelectCriteriaQuery(query);
		TypedQuery<T> q = entityManager.createQuery(cq);
		q.setFirstResult(query.getOffset());
		q.setMaxResults(query.getLimit());
		List<T> list = q.getResultList();
		return list.parallelStream();
	}

Issue solved, thank you for your assistance.

With Vaadin 13.0.7 same Error here, but different Situation. We’ve one Session in a Browser, showing the Status of some Data in a Grid. There a several other Sessions changeing Data (moving pallets in logistics) on move they send a Message via ActiveMQ and the Grid should Update Data.

We extend AbstractBackEndDataProvider and return a parallelStream, also tested with stream, but same Error:

java.lang.IllegalStateException: Cannot access state in VaadinSession or UI without locking the session.
	at com.vaadin.flow.server.VaadinSession.checkHasLock(VaadinSession.java:523)
	at com.vaadin.flow.server.VaadinSession.checkHasLock(VaadinSession.java:537)
	at com.vaadin.flow.internal.StateTree.checkHasLock(StateTree.java:390)
	at com.vaadin.flow.internal.StateTree.beforeClientResponse(StateTree.java:321)
	at com.vaadin.flow.data.provider.DataCommunicator.lambda$requestFlush$2f364bb9$2(DataCommunicator.java:425)
	at com.vaadin.flow.internal.StateNode.runWhenAttached(StateNode.java:807)
	at com.vaadin.flow.data.provider.DataCommunicator.requestFlush(DataCommunicator.java:424)
	at com.vaadin.flow.data.provider.DataCommunicator.reset(DataCommunicator.java:176)
	at com.vaadin.flow.data.provider.DataCommunicator.lambda$handleAttach$425c8a01$1(DataCommunicator.java:398)
	at com.vaadin.flow.data.provider.AbstractDataProvider.lambda$fireEvent$2(AbstractDataProvider.java:96)
	at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
	at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
	at java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1699)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
	at com.vaadin.flow.data.provider.AbstractDataProvider.fireEvent(AbstractDataProvider.java:94)
	at com.vaadin.flow.data.provider.AbstractDataProvider.refreshAll(AbstractDataProvider.java:53)
	at de.pelzgroup.ui.dataproviders.AbstractPelzDataProvider.refreshAll(AbstractPelzDataProvider.java:129)
	at de.pelzgroup.albatros.desktop.logistik.lager.Verladekontrolle.onMessage(Verladekontrolle.java:597)

Error occures on calling refreshItem too.

Hi Dirk,

You need to call refreshItem from within the thread that handles the current HTTP request (it automatically has the needed lock) or use UI.access or VaadinSession.access if you are running inside a background thread.

-Petter-

Hi Petter,

I wrote my Update in this Code:

	this.getUI().get().access(() ->  {
		vkdp.updateItem(messageReferenz);
		vkdp.refreshAll();
	});

vkdp is an Instance of AbstractBackEndDataProvider

The Exception has gone, but the Grid updates not with a Push, only if the user selects a Item or do something else witch causes an interaction with Backend.

-_Dirk -

Have you added the @Push annotation to your route?

-Petter-

Root cause in my case was that the dataprovider returned a parallel stream, when returning a normal stream and executing the update within an ui.access() it worked as it should.

Thanks at All! Great Job, now it works as expected! The @Push was the final Tip and calling this.getUI().get().push(); in access.
Have a nice Weekend!

Nice to hear you got it working!

The access() method should handle pushing automatically unless it has been explicitly disabled (i.e. there should not be a need to manually call the push() method).

-Petter-