8.6 KiB
STOMP Messaging Integration
Die Anwendung unterstützt jetzt STOMP (Simple Text Oriented Messaging Protocol) für die Kommunikation mit externen Apps über WebSocket-Verbindungen.
Übersicht
Das System bietet folgende STOMP-Funktionalitäten:
WebSocket-Endpunkte
/ws- STOMP-Endpunkt mit SockJS-Fallback-Unterstützung/websocket- Reiner WebSocket-Endpunkt ohne SockJS
Nachrichtendestinationen
Eingehende Nachrichten (Client → Server)
/app/message- Allgemeine Nachrichten/app/job/status- Job-Status-Updates/app/device/location- Gerätestandort-Updates/app/auth/login- Anmeldung eines App-Users (Payload: { email, password })
Ausgehende Nachrichten (Server → Client)
/topic/messages- Broadcast aller allgemeinen Nachrichten/topic/job-updates- Job-Status-Updates für alle Abonnenten/topic/device-locations- Gerätestandort-Updates/topic/broadcasts- System-weite Broadcast-Nachrichten/queue/notifications- Benutzerspezifische Benachrichtigungen
Verwendung für Apps
1. Verbindung aufbauen
// Mit SockJS
const socket = new SockJS('http://192.168.180.196:8080/ws');
const stompClient = Stomp.over(socket);
// Oder mit nativem WebSocket (WICHTIG: ws:// verwenden, nicht http://)
const socket = new WebSocket('ws://192.168.180.196:8080/websocket');
const stompClient = Stomp.over(socket);
Flutter/Dart STOMP Client
Für Flutter-Apps verwenden Sie diese Konfiguration:
import 'package:stomp_dart_client/stomp.dart';
import 'package:stomp_dart_client/stomp_config.dart';
import 'package:stomp_dart_client/stomp_frame.dart';
import 'dart:convert';
// WICHTIG: Verwenden Sie ws:// für WebSocket-Verbindungen, NICHT http://
final stompClient = StompClient(
config: StompConfig(
url: 'ws://192.168.180.196:8080/websocket', // Beachten Sie ws:// statt http://
onConnect: onConnectCallback,
onWebSocketError: (dynamic error) => print('WebSocket Error: $error'),
onStompError: (StompFrame frame) => print('Stomp Error: ${frame.body}'),
onDisconnect: (StompFrame frame) => print('Disconnected'),
// Heartbeat-Konfiguration
heartbeatIncoming: Duration(seconds: 20),
heartbeatOutgoing: Duration(seconds: 20),
),
);
void onConnectCallback(StompFrame frame) {
print('Connected to STOMP server');
// Nachrichten abonnieren
stompClient.subscribe(
destination: '/topic/messages',
callback: (StompFrame frame) {
print('Received message: ${frame.body}');
},
);
}
// Verbindung herstellen
stompClient.activate();
// Nachricht senden
void sendMessage(String content) {
stompClient.send(
destination: '/app/message',
body: jsonEncode({
'content': content,
'sender': 'FlutterApp',
}),
);
}
Alternative Endpunkte für Flutter:
Falls Probleme mit /websocket auftreten, versuchen Sie:
ws://192.168.180.196:8080/stomp(zusätzlicher Endpunkt)ws://192.168.180.196:8080/ws(SockJS-Endpunkt ohne SockJS-Protokoll)
2. Verbindung herstellen
stompClient.connect({}, function(frame) {
console.log('Verbunden: ' + frame);
// Nachrichten abonnieren
stompClient.subscribe('/topic/messages', function(message) {
console.log('Nachricht erhalten:', JSON.parse(message.body));
});
});
3. Nachrichten senden
// Allgemeine Nachricht senden
stompClient.send('/app/message', {}, JSON.stringify({
content: 'Hallo vom App',
sender: 'MobileApp'
}));
// Job-Status-Update senden
stompClient.send('/app/job/status', {}, JSON.stringify({
jobId: '12345',
status: 'IN_PROGRESS',
progress: 75
}));
// Gerätestandort senden
stompClient.send('/app/device/location', {}, JSON.stringify({
deviceId: 'device-001',
latitude: 52.5200,
longitude: 13.4050,
accuracy: 10
}));
// Anmeldung eines App-Users
// Zuerst die Antwort-Warteschlange abonnieren (user-spezifisch)
const authSubscription = stompClient.subscribe('/user/queue/auth', function(message) {
const resp = JSON.parse(message.body);
console.log('Login-Antwort:', resp);
});
// Login-Request senden
stompClient.send('/app/auth/login', {}, JSON.stringify({
email: 'user@example.com',
password: 'geheimesPasswort'
}));
Backend-Integration
Programmatische Nachrichten senden
@Autowired
private MessageController messageController;
// Benachrichtigung an spezifischen Benutzer
messageController.sendNotificationToUser("username", "Neue Aufgabe verfügbar");
// Broadcast-Nachricht an alle
messageController.sendBroadcastMessage("Systemwartung in 10 Minuten");
Zeroconf (mDNS) Veröffentlichung
Die Anwendung veröffentlicht die STOMP-Schnittstelle via Zeroconf (DNS-SD/mDNS), sofern verfügbar. Es wird der Service-Typ _stomp._tcp.local. mit folgenden TXT-Records publiziert:
- path = Pfad für SockJS-Endpoint (Standard: /ws)
- websocket = Pfad für nativen WebSocket (Standard: /websocket)
- protocol = "stomp"
Clients können per Bonjour/mDNS nach _stomp._tcp suchen und erhalten Port und Metadaten.
Hinweise:
- Die Implementierung nutzt JmDNS, falls die Bibliothek auf dem Klassenpfad vorhanden ist. In Umgebungen ohne JmDNS bleibt Zeroconf stillschweigend deaktiviert (es wird ein Hinweis im Log ausgegeben).
- Konfigurierbare Properties:
- app.zeroconf.enabled (default: true)
- app.zeroconf.serviceName (default: votianlt-stomp)
- app.stomp.wsPath (default: /ws)
- app.stomp.websocketPath (default: /websocket)
Konfiguration
Die STOMP-Konfiguration befindet sich in:
WebSocketConfig.java- WebSocket und STOMP-KonfigurationMessageController.java- Nachrichtenbehandlungapplication.properties- Zusätzliche WebSocket-Einstellungen
Wichtige Konfigurationsparameter
# Nachrichtenpuffergröße
spring.websocket.servlet.max-text-message-buffer-size=8192
spring.websocket.servlet.max-binary-message-buffer-size=8192
# STOMP aktivieren
spring.websocket.stomp.enabled=true
# Heartbeat-Einstellungen
spring.websocket.stomp.heartbeat.outgoing=10000
spring.websocket.stomp.heartbeat.incoming=10000
Sicherheitshinweise
- WebSocket-Verbindungen verwenden die gleiche Authentifizierung wie die Web-Anwendung
- Nachrichten werden automatisch mit Zeitstempel versehen
- Alle Nachrichten werden in JSON-Format verarbeitet
Testing
Zum Testen der STOMP-Funktionalität können Sie:
- Eine WebSocket-Client-Bibliothek verwenden
- Browser-Entwicklertools für WebSocket-Verbindungen nutzen
- Spezialisierte STOMP-Testing-Tools einsetzen
Die Implementierung ist vollständig und bereit für die Integration mit externen Apps.
Neue STOMP-Schnittstelle: Task-Erledigung melden
Mit dieser Schnittstelle kann ein Client die Erledigung eines Tasks melden.
- Senden (Client → Server):
/app/task/completed - Broadcasts (Server → Client):
- Global:
/topic/task-updates - Task-spezifisch:
/topic/tasks/{taskId}
- Global:
Payload (JSON):
{
"taskId": "<MongoId als String>",
"completedBy": "<optional: Name/ID des Ausführenden>",
"note": "<optional: Kommentar>"
}
Antwort (Beispiel):
{
"timestamp": "2025-09-05T09:25:00",
"type": "taskCompletedAck",
"success": true,
"taskId": "...",
"jobId": "...",
"completed": true,
"completedAt": "2025-09-05T09:25:00",
"completedBy": "driver01",
"note": "Übergabe erfolgreich",
"event": "taskCompleted"
}
JavaScript Beispiel:
// Abonnieren der globalen Updates
stompClient.subscribe('/topic/task-updates', (frame) => {
console.log('Task update:', JSON.parse(frame.body));
});
// Abonnieren eines spezifischen Tasks
stompClient.subscribe('/topic/tasks/' + taskId, (frame) => {
console.log('Task-specific update:', JSON.parse(frame.body));
});
// Task als erledigt melden
stompClient.send('/app/task/completed', {}, JSON.stringify({
taskId: taskId,
completedBy: 'driver01',
note: 'Übergabe erfolgreich'
}));
Flutter/Dart Beispiel:
stompClient.subscribe(
destination: '/topic/task-updates',
callback: (frame) => print('Task update: ${frame.body}'),
);
stompClient.subscribe(
destination: '/topic/tasks/$taskId',
callback: (frame) => print('Task-specific update: ${frame.body}'),
);
stompClient.send(
destination: '/app/task/completed',
body: jsonEncode({
'taskId': taskId,
'completedBy': 'driver01',
'note': 'Übergabe erfolgreich',
}),
);
Hinweise:
taskIdist Pflicht. Bei ungültiger oder unbekanntertaskIdwirdsuccess=falsezurückgegeben.- Der Server setzt
completed=trueundcompletedAtautomatisch. - Zusätzlich zum globalen Broadcast wird ein task-spezifisches Event auf
/topic/tasks/{taskId}versendet.