Vaadin CDI + Event + Push = NoSuchElementException: No value present - What

Hi there,

I have a client websocket which receives messages from a server. I want to update my view every time a messages is received. But unfortunately it does not work. I get a NoSuchElementException: No value present when accessing the UI to do the update. When I use fireAsync()/@ObserveAsyncinstead of fire()/@Observe I even do not get an excecption at all (nor is my view updated).

Can someone please guide me in the right direction. I am kind of new to CDI. Maybe I missed something?

I am using Vaadin CDI + Event + Push.

  • Vaadin Version 14.1.28
  • Vaadin CDI 11.0.1
  • Java EE 8.0.1
  • WildFly 18.0.0

My view class, which shall be update:

import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.Command;
import com.vaadin.flow.server.PWA;
import com.vaadin.flow.shared.communication.PushMode;

import javax.annotation.PostConstruct;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

@Route("")
@PWA(name = "Project Base for Vaadin Flow with CDI", shortName = "Project Base", enableInstallPrompt = false)
@CssImport("./styles/shared-styles.css")
@CssImport(value = "./styles/vaadin-text-field-styles.css", themeFor = "vaadin-text-field")
@Push(PushMode.MANUAL)
public class MainView extends VerticalLayout {

    @Inject
    private ClientWebsocket clientWebsocket;

    private Button buConnect;
    private Button buDisconnect;
    private Label label;
    
    @PostConstruct
    public void init() {
        
        buConnect = new Button("Connect",  new ComponentEventListener<ClickEvent<Button>>() {
            @Override
            public void onComponentEvent(ClickEvent<Button> t) {
                clientWebsocket.connect();
            }
        });
        add(buConnect);
        
        buDisconnect = new Button("DisConnect",  new ComponentEventListener<ClickEvent<Button>>() {
            @Override
            public void onComponentEvent(ClickEvent<Button> t) {
                clientWebsocket.disconnect();
            }
        });
        add(buDisconnect);
        
        label = new Label("Message: ");
        add(label);
    }
    
    //public void updateView(@ObservesAsync MessageReceivedEvent messageReceivedEvent) {
    public void updateView(@Observes MessageReceivedEvent messageReceivedEvent) {
        System.out.println("Updating view.");
        getUI().get().access(new Command() { // <-- This is where the exception gets thrown
            @Override
            public void execute() {
                label.setText("Message: " + messageReceivedEvent.getMessage().getMessage());
                getUI().get().push();
            }
        });
    }

}

My websocket class:

import com.vaadin.cdi.annotation.VaadinSessionScoped;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;


@VaadinSessionScoped
@ClientEndpoint(decoders = {MessageDecoder.class}, encoders = {MessageEncoder.class})
public class ClientWebsocket implements Serializable {
    
    private String uri = "ws://localhost:8080/TestWebsocketsServer-1.0-SNAPSHOT/websockettest1"; 
    
    private Session session = null;
    
    @Inject
    private Event<MessageReceivedEvent> messageReceivedEvent;
    
    public ClientWebsocket() {
    }
    
    public boolean connect() {
        try {
            WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();
            webSocketContainer.connectToServer(this, new URI(uri));
            return true;
        } catch (URISyntaxException | DeploymentException | IOException ex) {
            Logger.getLogger(ClientWebsocket.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }
    
    public boolean disconnect() {
        try {
            session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Closed on user request."));
            return true;
        } catch (IOException ex) {
            Logger.getLogger(ClientWebsocket.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }
    
    public boolean isClosed() {
        return session == null || !session.isOpen();
    }
    
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
    }
    
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        this.session = null;
    }
    
    @OnMessage
    public void onMessage(Message message) {
        System.out.println("Client - Received message: " + message.getMessage());
        messageReceivedEvent.fireAsync(new MessageReceivedEvent(message));
    }
	
}

The exception I get:

10:43:23,308 INFO  [stdout]
 (default task-60) Received message: Counter: 50

10:43:23,308 INFO  [stdout]
 (default task-60) Updating view.

10:43:23,308 ERROR [io.undertow.websockets.core.request]
 (default task-60) UT025007: Unhandled exception for annotated endpoint com.packagename.myapp.ClientWebsocket@10aad88c: java.util.NoSuchElementException: No value present
	at java.base/java.util.Optional.get(Optional.java:141)
	at deployment.my-starter-project-1.0-SNAPSHOT.war//com.packagename.myapp.MainView.updateView(MainView.java:65)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:95)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.injection.StaticMethodInjectionPoint.invoke(StaticMethodInjectionPoint.java:85)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.injection.MethodInvocationStrategy$SimpleMethodInvocationStrategy.invoke(MethodInvocationStrategy.java:168)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:330)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:308)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.event.ObserverMethodImpl.notify(ObserverMethodImpl.java:286)
	at javax.enterprise.api//javax.enterprise.inject.spi.ObserverMethod.notify(ObserverMethod.java:124)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.util.Observers.notify(Observers.java:166)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.event.ObserverNotifier.notifySyncObservers(ObserverNotifier.java:285)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.event.ObserverNotifier.notify(ObserverNotifier.java:273)
	at org.jboss.weld.core@3.1.2.Final//org.jboss.weld.event.EventImpl.fire(EventImpl.java:96)
	at deployment.my-starter-project-1.0-SNAPSHOT.war//com.packagename.myapp.ClientWebsocket.onMessage(ClientWebsocket.java:81)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at io.undertow.websocket@2.0.26.Final//io.undertow.websockets.jsr.annotated.BoundMethod.invoke(BoundMethod.java:87)
	at io.undertow.websocket@2.0.26.Final//io.undertow.websockets.jsr.annotated.AnnotatedEndpoint$2.onMessage(AnnotatedEndpoint.java:142)
	at io.undertow.websocket@2.0.26.Final//io.undertow.websockets.jsr.FrameHandler$7.run(FrameHandler.java:284)
	at io.undertow.websocket@2.0.26.Final//io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:170)
	at io.undertow.websocket@2.0.26.Final//io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:167)
	at io.undertow.servlet@2.0.26.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
	at org.wildfly.extension.undertow@18.0.0.Final//org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
	at org.wildfly.extension.undertow@18.0.0.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1504)
	at org.wildfly.extension.undertow@18.0.0.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1504)
	at org.wildfly.extension.undertow@18.0.0.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1504)
	at org.wildfly.extension.undertow@18.0.0.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1504)
	at io.undertow.websocket@2.0.26.Final//io.undertow.websockets.jsr.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:604)
	at io.undertow.websocket@2.0.26.Final//io.undertow.websockets.jsr.ServerWebSocketContainer$6.run(ServerWebSocketContainer.java:590)
	at io.undertow.websocket@2.0.26.Final//io.undertow.websockets.jsr.OrderedExecutor$ExecutorTask.run(OrderedExecutor.java:67)
	at org.jboss.threads@2.3.3.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
	at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
	at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
	at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
	at java.base/java.lang.Thread.run(Thread.java:830)

I think it is issue with scopes. Your endpoint class is VaadinSessionScoped. In VaadinSession there can be multiple UI’s (usually one, but there can be). I.e. when event is fired, session does not know to which UI to deliver it.

Your implementation looks strange, I’ve not seen something like this before. What you are trying to achieve?

I tried different scopes. Unfortunately without success. I either get a NoSuchElementException or no exception at all.

As said, I am trying to achieve to update my view every time I receive a message via websocket. Therefore I setup a server which increments a counter and publishes the number via a websocket. The client receives the message (again via websocket) and shall display the number.

Of course this is just a test case for me to find out how to do it and how things work. In the reald world things are different. But it comes down to receive a message from a server via websocket (this is given I cannot change that) and display its content in Vaadin.

It looks to me that this must have something to do with CDI events.

When I put everything in the view class and do not fire the event but call my updateView method directly everything works as expected. But when I use fire/@Observe I get the NoSuchMethodException.

import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.Command;
import com.vaadin.flow.server.PWA;
import com.vaadin.flow.shared.communication.PushMode;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

@Route("")
@PWA(name = "Project Base for Vaadin Flow with CDI", shortName = "Project Base", enableInstallPrompt = false)
@CssImport("./styles/shared-styles.css")
@CssImport(value = "./styles/vaadin-text-field-styles.css", themeFor = "vaadin-text-field")
@Push(PushMode.MANUAL)
@ClientEndpoint(decoders = {MessageDecoder.class}, encoders = {MessageEncoder.class})
public class MainView extends VerticalLayout {
   
    private final String uri = "ws://localhost:8080/TestWebsocketsServer-1.0-SNAPSHOT/websockettest1"; 
    Session session;
    
	@Inject
    private Event<MessageReceivedEvent> messageEvent;

    private Button buConnect;
    private Button buDisconnect;
    private Label label;
    
    @PostConstruct
    public void init() {  
        buConnect = new Button("Connect",  new ComponentEventListener<ClickEvent<Button>>() {
            @Override
            public void onComponentEvent(ClickEvent<Button> t) {
                connect();
            }
        });
        add(buConnect);
        
        buDisconnect = new Button("DisConnect",  new ComponentEventListener<ClickEvent<Button>>() {
            @Override
            public void onComponentEvent(ClickEvent<Button> t) {
                disconnect();
            }
        });
        add(buDisconnect);
        
        label = new Label("Message: ");
        add(label);
    }
	
    public void updateView(@Observes MessageReceivedEvent messageReceivedEvent) {
        System.out.println("MainView - Updating view.");
        getUI().get().access(new Command() { // <-- This is where the exception gets thrown
            @Override
            public void execute() {
                label.setText("Message: " + messageReceivedEvent.getMessage().getMessage());
                getUI().get().push();
            }
        });
    }
    
    public boolean connect() {
        try {
            WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();
            webSocketContainer.connectToServer(this, new URI(uri));
            return true;
        } catch (URISyntaxException | DeploymentException | IOException ex) {
            Logger.getLogger(ClientWebsocket.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }
    
    public boolean disconnect() {
        try {
            session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Closed on user request."));
            return true;
        } catch (IOException ex) {
            Logger.getLogger(ClientWebsocket.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }
    
    public boolean isClosed() {
        return session == null || !session.isOpen();
    }
    
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
    }
    
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        this.session = null;
    }
    
    @OnMessage
    public void onMessage(Message message) {
        System.out.println("Client - Received message: " + message.getMessage());
        updateView(new MessageReceivedEvent(message)); // <-- This works!
		//messageEvent.fire(new MessageReceivedEvent(message)); <-- This does not!
    }  
  
}

I don’t get it. Leaving the whole websocket stuff aside does not work either.

When I take the CDI-Starter project and adjust the GreetService in a way it fires CDI events I get the NoSuchElementException: No value present as well.

Any help on this is greatly appreciated.

The adjusted MainView class from the CDI-Starter project:

@Route("")
@PWA(name = "Project Base for Vaadin Flow with CDI", shortName = "Project Base", enableInstallPrompt = false)
@CssImport("./styles/shared-styles.css")
@CssImport(value = "./styles/vaadin-text-field-styles.css", themeFor = "vaadin-text-field")
@Push(PushMode.MANUAL)
public class MainView extends VerticalLayout {

    @Inject
    private GreetService greetService;
    
    private TextField textField;
    private Button button;
    private Label lblDisplay;

    @PostConstruct
    public void init() {
        textField = new TextField("Your name");

        button = new Button("Say hello", new ComponentEventListener<ClickEvent<Button>>() {
            @Override
            public void onComponentEvent(ClickEvent<Button> t) {
                //Notification.show(greetService.greet(textField.getValue()));
                greetService.greetCdi(textField.getValue());
            }
            
        });
		
		lblDisplay = new Label("Message: ");

        add(textField, button, lblDisplay);
        
    
    public void updateView(@Observes MessageReceivedEvent messageReceivedEvent) {
        System.out.println("MainView - Updating view.");
        getUI().get().access(new Command() { // <-- This is where the exception gets thrown
            @Override
            public void execute() {
                lblDisplay.setText("Message: " + messageReceivedEvent.getMessage().getMessage());
                getUI().get().push();
            }
        });        
    }

}

The adjusted GreetService class from the CDI-Starter project:

import com.vaadin.cdi.annotation.UIScoped;
import com.vaadin.cdi.annotation.VaadinSessionScoped;
import javax.inject.Inject;

//@VaadinSessionScoped
@UIScoped
public class GreetService {
    
    @Inject
    private javax.enterprise.event.Event<MessageReceivedEvent> messageEvent;

    public void greetCdi(String name) {
        if (name == null || name.isEmpty()) {
            messageEvent.fire(new MessageReceivedEvent(new Message("Hello anonymous user")));
        } else {
            messageEvent.fire(new MessageReceivedEvent(new Message("Hello " + name)));
        }    
    }
    
}

I got the above working. @UIScoped annotation was missing in the MainView. My bad.

Anyhow still no luck with websockets. The MainView does not recognize the fired event. :frowning:

Got it. Although it does not feel that this is the right way to do it… …it works.
Hopefully it still does when I have several updates (pushes) per second.

MainView:

@Route("")
@UIScoped
@Push(PushMode.MANUAL)
@PWA(name = "Project Base for Vaadin Flow with CDI", shortName = "Project Base", enableInstallPrompt = false)
@CssImport("./styles/shared-styles.css")
@CssImport(value = "./styles/vaadin-text-field-styles.css", themeFor = "vaadin-text-field")
public class MainView extends HorizontalLayout {

    @Inject
    private GreetView greetView;
    
    @Inject 
    private WebsocketView websocketView;
    
    @Inject 
    private WebsocketClient websocketClient;
    
    @PostConstruct
    public void init() {
        add(greetView);
        add(websocketView);
    }
    
    @Override
    protected void onAttach(AttachEvent attachEvent) {
        websocketClient.setUI(attachEvent.getUI());
    }   
}

WebsocketClient:

@UIScoped
@ClientEndpoint(decoders = {MessageDecoder.class}, encoders = {MessageEncoder.class})
public class WebsocketClient implements Serializable {
    
    private final String uri = "ws://localhost:8080/TestWebsocketsServer-1.0-SNAPSHOT/websockettest1"; 
    
    private Session session = null;
    
    @Inject
    private javax.enterprise.event.Event<MessageReceivedEvent> messageEvent;
    
    private UI ui;
    
    public WebsocketClient() {
    }

    public void setUI(UI ui) {
        this.ui = ui;
    }
	
    @OnMessage
    public void onMessage(Message message) {
        System.out.println("Client - Received message: " + message.getMessage());
        ui.access(new Command() {
            @Override
            public void execute() {
                messageEvent.fire(new MessageReceivedEvent(message));
            }
        });
    }	
	
	....
	..

Yes. As I mentioned in the first comment, it is about scopes. CDI Event can be observed in greater scope, but not in the smaller one. It is implementation limitation in Java EE / CDI, nothing to do with Vaadin.

See e.g. https://stackoverflow.com/questions/22684754/cdi-how-to-broadcast-events-between-dependent-beans

What you have done is essentially the same as discussed here https://github.com/vaadin/cdi/issues/226

If you need something that is like broadcast event, then I would ditch CDI events and use event bus instead, which I can produce in suitable scope and inject to view where I want to listen those broadcast events.