WildFly WebSocket-Server Kommunikation mit der BusinessLogic


#1

Hi,

ich versuche mich gerade im Rahmen vom Studium mit Java EE zu beschäftigen.
Als Aufgabe sollten wir einen WebSocket-Server erstellen. Um das ganze einfach zu halten, haben wir alles in der WebSocket-Server-Klasse abgebildet (Server-Methoden + BusinessLogic).

Mir hat sich jetzt aber die Frage gestellt, wie extrahiert man die BusinessLogic in eine andere Klasse, um die Komponenten sauber voneinander zu trennen?

Wie kann ich von einer BusinessLogic-Klasse Daten über den WebSocket-Server an eine Session senden und wie kann ich der BusinessLogic-Klasse informationen vom WebSocket-Server zu spielen?

Es wäre echt super von euch, wenn ihr mir das mal bitte erklären könntet. Es reicht mir auch grob um rissen, so das ich auf diesen Informationen Dr. Google weiter bemühen kann :wink:

Vielen Dank schon mal vorab :slightly_smiling_face:

Grüße Peter


#2

Hallo Peter,

ich bin mir nicht ganz sicher was du von der Forengemeinde jetzt hören willst.

Der einfachste Weg wäre sicher die Geschäftslogik im WebSocket-Server zu instanzieren und dann darauf zuzugreifen. Somit kannst du von den Methoden des WebSocket-Servers direkt auf die Geschäftslogik zugreifen. Der Weg von der Geschäftslogik zum WebSocket-Server kannst du über eine public static Methode im WebSocket-Server erreichen. Über diese öffentlich erreichbare Methode kann deine Geschäftslogik dann Daten an die WebSocket-Clients versenden.

Diese eben beschriebende Möglichkeit, sollte für deine Zwecke vollkommen ausreichend sein.

Aber wenn du schon fragst, wie man es machen kann, möchte ich dir noch eine zweite Möglichkeit aufzeigen, die aber komplexer ist. Wenn man sich das Szenario vorstellt, das man eine Anwendung haben möchte, wo die Geschäftslogik nicht nur mit einem WebSocket-Server, sondern auch mit einem zB. REST-Server interagieren will/muss (aus was für Grunden auch immer), kann man unter Anderem folgendes tun:

  1. erzeugen eines WebSocket-Server
  2. erzeugen eines REST-Endpoints (REST-Server)
  3. erzeugen der Geschäftslogik
  4. die Geschäftslogik injiziert (Stichwort CDI und @ApplicationScoped)

WebSocketSrv.java

// WebSocketSrv.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.PongMessage;
import javax.websocket.RemoteEndpoint.Basic;
import javax.inject.Inject;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/websocket")
public class WebSocketSrv {

	@Inject
	BusinessLogicTest blt;

	private static Set<Session> userSessions = Collections.newSetFromMap(new ConcurrentHashMap<Session, Boolean>());

	@OnOpen
	public void onOpen(Session session) throws IOException {
		userSessions.add(session);
		System.out.println("OnOpen " + session.getId());
	}

	@OnMessage
	public void onMessage(String message, Session session) throws IOException {
		System.out.println("OnMessage (" + session.getId() + "): " + message);

		Basic endpoint = session.getBasicRemote();
		if (message.equals("getLastMessage")) {
			endpoint.sendText(blt.getLastMessage());
		} else {
			blt.setMessage(message);
			endpoint.sendText("OK");
		}
	}

	@OnMessage
	public void receiveBinary(Session session, ByteBuffer binaryMsg) {
	}

	@OnMessage
	public void receivePong(Session session, PongMessage pongMsg) {
	}

	@OnClose
	public void onClose(Session session) throws IOException {
		userSessions.remove(session);
		System.out.println("OnClose " + session.getId());
	}

	@OnError
	public void onError(Session session, Throwable throwable) {
		System.out.println("OnError " + session.getId());
		System.out.println(throwable);
	}

	public static void broadcast(String msg) {
		System.out.println(userSessions.size());
		for (Session session : userSessions) {
			System.out.println("send Message to " + session.getId() + " -> " + msg);
			session.getAsyncRemote().sendText(msg);
		}

	}
}

RESTApplication.java

//RESTApplication.java
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/")
public class RESTApplication extends Application {

}

RestSrv.java

//RestSrv.java
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;

@Path("rest")
public class RestSrv {
	@Inject 
	BusinessLogicTest blt;

	@GET
	@Path("/")
	public String getMessage() {
		return blt.getLastMessage();
	}
	
	@POST
	@Path("/")
	public void setMessage(@QueryParam("msg") String msg) {
		blt.setMessage(msg);
	}
	
}

BusinessLogicTest.java

// BusinessLogicTest.java
import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class BusinessLogicTest {
	
	private String lastMessage = "";
	
	public void setMessage(String message) {
		if(message.equals("RouteMessateToAllWebSocketClients")) {
			WebSocketSrv.broadcast(this.lastMessage);
		} else {
			this.lastMessage = message;
		}
	}
	
	public String getLastMessage() {
		return this.lastMessage;
	}
}

In dem Beispiel kannst du mittels WebSocket oder REST eine Nachricht an den Server senden. Die letzte Nachricht wird in der Geschäftslogik zwischengespeichert. Wenn du über REST die Nachricht “RouteMessateToAllWebSocketClients” per POST sendetst, wird diese Nachricht nicht als Message sondern als Command angesehen und sorgt dafür, das die letze Nachricht an alle WebSocket-Clients versendet wird.

Hoffe das hilft dir erst mal.

@all
Da ich selber noch am lernen von Java EE bin und für mein Empfinden noch am absoluten Anfang der Kunst stehe, wäre es echt super, wenn ihr euch das Ganze auch noch mal angucken könntet und mich korrigiert, oder mir bessere Wege aufzeigt :wink: DANKE!

Grüße Hans


#3

Hallo noch einmal :wink:
Da ich meinen Beitrag nicht bearbeiten kann, muss ich hier noch mal was zur Änderung nachschieben.

Im Quellcode der BusinessLogicTest.java habe ich die Annotation @ApplicationScoped verwendet. Das scheint falsch zu sein. Wenn ich das jetzt richtig gelesen habe, ist hier @Singleton richtiger.

import javax.inject.Singleton;

@Singleton
public class BusinessLogicTest {
    ....
}

Hoffe das ist jetzt richtiger :innocent: aber wie gesagt, ich hoffe ja noch darauf, das erfahrene Entwickler sich hierzu noch mal äußern :see_no_evil:

Grüße