Erweiterungen

This commit is contained in:
2025-12-08 19:16:21 +01:00
parent e7d18533b5
commit c0085d8931
13 changed files with 101 additions and 2138 deletions

View File

@@ -1,269 +0,0 @@
# MQTT System Changelog
## Version 2.0.0 - 2025-10-22
### Breaking Changes
#### 1. MQTT-Broker-Port geändert
- **Alt**: Port `1883`
- **Neu**: Port `42099`
- **Broker**: `mqtt-2.assecutor.de:42099`
- **Grund**: Neuer dedizierter Port für die Produktionsumgebung
#### 2. Konfigurationssystem umgestellt
Die alte `app.mqtt.*` Konfiguration wurde durch das neue Plugin-System ersetzt.
**Entfernte Konfiguration:**
```properties
app.mqtt.enabled=true
app.mqtt.broker-uri=mqtt://mqtt-2.assecutor.de
app.mqtt.client-id=server-${random.uuid}
app.mqtt.clean-start=false
app.mqtt.session-expiry-interval=86400
app.mqtt.keep-alive=30
app.mqtt.max-inflight=50
app.mqtt.automatic-reconnect=true
app.mqtt.default-qos=2
app.mqtt.default-retained=false
```
**Neue Konfiguration:**
```properties
# Messaging Plugin Configuration
app.messaging.plugin.type=mqtt
app.messaging.plugin.mqtt.broker.host=mqtt-2.assecutor.de
app.messaging.plugin.mqtt.broker.port=42099
app.messaging.plugin.mqtt.username=app
app.messaging.plugin.mqtt.password=apppwd
app.messaging.plugin.mqtt.client.id=votianlt-server
```
### Neue Features
#### 1. Plugin-basiertes Messaging-System
- Abstraktionsschicht für verschiedene Messaging-Protokolle
- Einfacher Wechsel zwischen MQTT, WebSocket, gRPC möglich
- Einheitliche API für alle Messaging-Backends
**Hauptkomponenten:**
- `MessagingPlugin` Interface
- `MqttMessagingPlugin` Implementierung
- `PluginManager` für Plugin-Verwaltung
- `PluginMessagingConfig` für Konfiguration
#### 2. Message Envelope System
Alle Nachrichten werden jetzt in Envelopes verpackt:
```java
public class MessageEnvelope {
private String messageId; // UUID
private Instant timestamp; // Zeitstempel
private String topic; // MQTT-Topic
private Object payload; // Eigentliche Nachricht
private boolean requiresAck; // ACK erforderlich?
private int retryCount; // Anzahl Wiederholungen
private Instant expiresAt; // Ablaufzeitpunkt
}
```
#### 3. Acknowledgment-System
- Automatische ACK-Verwaltung
- Retry-Mechanismus bei fehlenden ACKs
- Konfigurierbare Timeouts und Wiederholungen
```java
public class AcknowledgmentMessage {
private String messageId;
private Instant timestamp;
private String status; // SUCCESS oder FAILED
private String errorMessage;
}
```
#### 4. Verbesserte Fehlerbehandlung
- Erhöhter Connection-Timeout: 30s → 60s
- Detaillierte Fehlerdiagnose:
- `TimeoutException`: Broker nicht erreichbar
- `UnknownHostException`: DNS-Fehler
- `ConnectException`: Port blockiert
- Automatische Wiederverbindung mit exponentieller Backoff
#### 5. Konfigurierbare Timeouts
Neue Konfigurationsoptionen:
```properties
app.messaging.plugin.mqtt.connection.timeout.seconds=60
app.messaging.plugin.mqtt.keep.alive.seconds=60
```
### Entfernte Komponenten
#### Gelöschte Klassen:
1. `MqttProperties.java` - Ersetzt durch Plugin-Konfiguration
2. `MqttConfig.java` - Nicht mehr benötigt
#### Grund für Entfernung:
Diese Klassen wurden durch das neue Plugin-System obsolet und waren nicht mehr in Verwendung.
### Geänderte Komponenten
#### 1. MqttMessagingPlugin
**Datei**: `src/main/java/de/assecutor/votianlt/messaging/plugin/mqtt/MqttMessagingPlugin.java`
**Änderungen:**
- Connection-Timeout von 30s auf 60s erhöht
- Konfigurierbare Timeout-Parameter hinzugefügt
- Verbesserte Fehlerbehandlung mit spezifischen Exception-Checks
- Detailliertes Logging für Verbindungsprobleme
```java
// Neu: Konfigurierbare Timeouts
private static final String CONFIG_CONNECTION_TIMEOUT = "connection.timeout.seconds";
private static final String CONFIG_KEEP_ALIVE = "keep.alive.seconds";
// Verbesserte Fehlerbehandlung
if (throwable instanceof java.util.concurrent.TimeoutException) {
log.error("[MqttPlugin] Connection timeout - broker may be unreachable");
} else if (throwable.getCause() instanceof java.net.UnknownHostException) {
log.error("[MqttPlugin] Unknown host - DNS resolution failed");
} else if (throwable.getCause() instanceof java.net.ConnectException) {
log.error("[MqttPlugin] Connection refused - broker may be down");
}
```
#### 2. PluginMessagingConfig
**Datei**: `src/main/java/de/assecutor/votianlt/messaging/config/PluginMessagingConfig.java`
**Änderungen:**
- Verwendet neue Konfigurationsparameter
- Initialisiert Plugin mit korrektem Port (42099)
- Setzt Authentifizierung (username/password)
#### 3. application.properties
**Datei**: `src/main/resources/application.properties`
**Änderungen:**
- Alte `app.mqtt.*` Konfiguration entfernt (Zeilen 57-71)
- Neue `app.messaging.plugin.mqtt.*` Konfiguration verwendet
- Port auf 42099 aktualisiert
### Architektur-Änderungen
#### Neue Architektur-Schichten:
```
Application Layer (Services, Controllers)
MessageDeliveryService (Envelope-Wrapping, Queue-Management)
PluginManager (Plugin-Verwaltung)
MessagingPlugin Interface (Protokoll-Abstraktion)
MqttMessagingPlugin (MQTT-spezifische Implementierung)
HiveMQ MQTT Client
```
#### Vorteile der neuen Architektur:
1. **Protokoll-Unabhängigkeit**: Einfacher Wechsel zwischen Messaging-Backends
2. **Testbarkeit**: Mock-Plugins für Tests
3. **Wartbarkeit**: Klare Trennung der Verantwortlichkeiten
4. **Erweiterbarkeit**: Neue Protokolle einfach hinzufügbar
### Migration Guide
#### Server-Migration (bereits durchgeführt):
1. ✅ Port auf 42099 geändert
2. ✅ Alte Konfiguration entfernt
3. ✅ Neue Plugin-Konfiguration aktiviert
4. ✅ Timeout erhöht
5. ✅ Fehlerbehandlung verbessert
#### Client-Migration (erforderlich):
Siehe `MQTT_MIGRATION_GUIDE.md` für detaillierte Anweisungen.
**Wichtigste Schritte:**
1. Port auf 42099 ändern
2. Authentifizierung hinzufügen (username: `app`, password: `apppwd`)
3. Message-Envelope-Format implementieren
4. ACK-Handling implementieren
5. Timeout auf 60s erhöhen
### Kompatibilität
#### Abwärtskompatibilität:
- ✅ Topic-Struktur unverändert
- ✅ Legacy-Nachrichten werden noch unterstützt
- ⚠️ Neue Clients sollten Envelope-Format verwenden
#### Vorwärtskompatibilität:
- ✅ Neue Features sind optional
- ✅ Schrittweise Migration möglich
- ✅ Alte und neue Clients können parallel laufen
### Testing
#### Getestete Szenarien:
1. ✅ Verbindung zum neuen Broker (mqtt-2.assecutor.de:42099)
2. ✅ Authentifizierung mit username/password
3. ✅ Subscription zu allen Topics
4. ✅ Message-Envelope-Verarbeitung
5. ✅ ACK-Handling
6. ✅ Automatische Wiederverbindung
7. ✅ Timeout-Handling
#### Test-Logs:
```
11:29:26.251 [RxComputationThreadPool-1] INFO MqttMessagingPlugin - Connected successfully
11:29:26.414 [RxComputationThreadPool-3] INFO MqttMessagingPlugin - Successfully subscribed to: /ack/server/+
11:29:26.414 [RxComputationThreadPool-4] INFO MqttMessagingPlugin - Successfully subscribed to: /server/+/task_completed
11:29:26.414 [RxComputationThreadPool-5] INFO MqttMessagingPlugin - Successfully subscribed to: /server/+/jobs/assigned
11:29:26.415 [RxComputationThreadPool-6] INFO MqttMessagingPlugin - Successfully subscribed to: /server/+/message
11:29:26.417 [RxComputationThreadPool-7] INFO MqttMessagingPlugin - Successfully subscribed to: /server/+/login
```
### Performance
#### Verbindungsaufbau:
- **Vorher**: ~30s bis Timeout bei Fehlern
- **Nachher**: ~60s bis Timeout, aber schnellere Fehlerdiagnose
#### Nachrichtendurchsatz:
- Keine Änderung (QoS 2, exactly once)
- Envelope-Overhead: ~200 Bytes pro Nachricht
### Bekannte Probleme
#### Keine bekannten Probleme
Alle Tests erfolgreich durchgeführt.
### Nächste Schritte
1. **Client-Migration**: Mobile Apps auf neuen Port und Envelope-Format umstellen
2. **Monitoring**: Metriken für Message-Delivery-Service hinzufügen
3. **Dokumentation**: API-Dokumentation für Envelope-Format erweitern
4. **Testing**: Integration-Tests für verschiedene Fehlerszenarien
### Rollback-Plan
Falls Probleme auftreten:
1. **Port zurücksetzen**:
```properties
app.messaging.plugin.mqtt.broker.port=1883
```
2. **Alte Konfiguration wiederherstellen**:
- `MqttProperties.java` aus Git-Historie wiederherstellen
- `MqttConfig.java` aus Git-Historie wiederherstellen
- Alte `app.mqtt.*` Konfiguration in `application.properties` einfügen
3. **Server neu starten**
### Kontakt
Bei Fragen oder Problemen:
- Siehe `MQTT_MIGRATION_GUIDE.md` für Client-Migration
- Siehe `MESSAGING_LAYER.md` für Architektur-Details
- Siehe `CLAUDE.md` für allgemeine System-Dokumentation

View File

@@ -1,9 +0,0 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to http://unlicense.org

View File

@@ -1,318 +0,0 @@
# Message Delivery Layer - Implementierungsdokumentation
## Übersicht
Der **Message Delivery Layer** ist eine neue Abstraktionsschicht zwischen der Anwendungslogik und dem MQTT-Transport. Er bietet:
-**Zuverlässige Nachrichtenzustellung** mit Bestätigungsmechanismus
-**Automatische Wiederholungsversuche** bei fehlgeschlagenen Zustellungen
-**Protokoll-Abstraktion** für einfachen Austausch von MQTT gegen andere Transporte
-**Vollständiges Audit-Log** aller Nachrichten
-**Queue-basiertes Design** für Skalierbarkeit
## Architektur
```
Application Layer (MessageController, Services)
MessageDeliveryService (Envelope-Wrapping, Queue-Management)
MessagingTransport Interface (Protokoll-Abstraktion)
MqttTransportAdapter (MQTT-spezifische Implementierung)
MqttV5ClientManager (HiveMQ Client)
```
## Komponenten
### 1. Datenmodelle (`messaging/model/`)
#### MessageEnvelope
Wrapper für alle Nachrichten mit Metadaten:
- `messageId`: Eindeutige UUID
- `timestamp`: Erstellungszeitpunkt
- `topic`: Ziel-Topic
- `payload`: Ursprüngliche Nachricht
- `requiresAck`: Bestätigung erforderlich?
- `retryCount`: Anzahl Wiederholungsversuche
- `expiresAt`: Ablaufzeitpunkt
#### PendingDelivery
Tracking-Objekt für ausstehende Zustellungen:
- `messageId`: Referenz zur Envelope
- `status`: PENDING, SENT, ACKNOWLEDGED, FAILED, EXPIRED
- `retryCount`: Aktuelle Wiederholungsversuche
- `nextRetryAt`: Zeitpunkt des nächsten Versuchs
- `envelopeData`: Serialisierte Envelope-Daten
#### AcknowledgmentMessage
Bestätigungsnachricht vom Client:
- `messageId`: ID der bestätigten Nachricht
- `status`: RECEIVED, PROCESSED, FAILED
- `clientId`: Absender der Bestätigung
- `errorMessage`: Optional bei Fehler
### 2. Transport Layer (`messaging/transport/`)
#### MessagingTransport Interface
Protokoll-agnostische Schnittstelle:
```java
CompletableFuture<Void> send(String topic, byte[] payload, TransportOptions options);
void subscribe(String topicPattern, MessageHandler handler);
boolean isConnected();
String getTransportType();
```
#### MqttTransportAdapter
MQTT-Implementierung des Transport-Interfaces:
- Adaptiert HiveMQ MQTT Client
- Unterstützt MQTT-Wildcards (+ und #)
- QoS-Mapping (0, 1, 2)
### 3. Delivery Service (`messaging/delivery/`)
#### MessageDeliveryService
Zentrale Orchestrierung:
- `sendMessage()`: Nachricht mit Tracking senden
- `handleIncomingMessage()`: Eingehende Envelope verarbeiten
- `handleAcknowledgment()`: Bestätigung verarbeiten
- `retryPendingDeliveries()`: Wiederholungsversuche durchführen
- `cleanupOldDeliveries()`: Alte Einträge aufräumen
#### AcknowledgmentHandler
Routing eingehender Nachrichten zur Anwendungslogik:
- Unwrapping der Envelope
- Routing basierend auf Topic-Pattern
- Delegation an MessageController
#### RetryScheduler
Geplante Tasks:
- Retry-Task: Alle 30 Sekunden (konfigurierbar)
- Cleanup-Task: Stündlich (konfigurierbar)
### 4. Repositories
#### MessageEnvelopeRepository
Speicherung aller Envelope-Objekte in MongoDB Collection `message_envelopes`
#### PendingDeliveryRepository
Tracking ausstehender Zustellungen in MongoDB Collection `pending_deliveries`
## Nachrichtenfluss
### Outbound (Server → Client)
1. **Anwendung** ruft `MessageDeliveryService.sendMessage()` auf
2. **MessageEnvelope** wird erstellt mit eindeutiger `messageId`
3. **PendingDelivery** wird in Datenbank gespeichert (Status: PENDING)
4. **Envelope** wird als JSON serialisiert
5. **Transport** sendet Nachricht via MQTT
6. **Status** wird auf SENT aktualisiert, `nextRetryAt` gesetzt
7. **Client** empfängt Nachricht und sendet ACK
8. **AcknowledgmentHandler** verarbeitet ACK
9. **PendingDelivery** wird auf ACKNOWLEDGED gesetzt
### Inbound (Client → Server)
1. **MQTT** empfängt Nachricht auf Topic
2. **MqttTransportAdapter** leitet an MessageDeliveryService weiter
3. **MessageEnvelope** wird aus JSON deserialisiert
4. **ACK** wird automatisch an Client gesendet (falls `requiresAck=true`)
5. **AcknowledgmentHandler** routet Payload zur Anwendung
6. **MessageController** verarbeitet Business-Logik
## Retry-Mechanismus
### Exponential Backoff
```
Versuch 1: 5 Sekunden
Versuch 2: 10 Sekunden (5s × 2.0)
Versuch 3: 20 Sekunden (10s × 2.0)
Versuch 4: 40 Sekunden (20s × 2.0)
Maximum: 5 Minuten
```
### Ablauf
1. **RetryScheduler** läuft alle 30 Sekunden
2. Findet alle `PendingDelivery` mit Status SENT und `nextRetryAt` in Vergangenheit
3. Prüft auf Ablauf (`expiresAt`) und Max-Retries
4. Sendet Nachricht erneut via Transport
5. Aktualisiert `retryCount` und `nextRetryAt`
## Konfiguration
### application.properties
```properties
# Message Delivery Layer Configuration
app.messaging.delivery.max-retries=3
app.messaging.delivery.retry-initial-delay=5s
app.messaging.delivery.retry-max-delay=5m
app.messaging.delivery.retry-backoff-multiplier=2.0
app.messaging.delivery.ack-timeout=30s
app.messaging.delivery.message-expiry=24h
app.messaging.delivery.cleanup-interval-minutes=60
app.messaging.delivery.retry-interval-seconds=30
app.messaging.delivery.acknowledged-retention-days=7
```
## MQTT Topics
### Outbound (Server → Client)
```
/client/{clientId}/message # Chat-Nachrichten (wrapped)
/client/{clientId}/jobs # Job-Zuweisungen (wrapped)
/client/{clientId}/auth # Login-Antworten (wrapped)
```
### Inbound (Client → Server)
```
/server/{clientId}/task_completed # Task-Abschlüsse (wrapped)
/server/{clientId}/message # Chat-Nachrichten (wrapped)
/server/{clientId}/jobs/assigned # Job-Anfragen (wrapped)
/server/login # Login-Anfragen (wrapped)
```
### Acknowledgments
```
/ack/server/{messageId} # Client → Server ACK
/ack/client/{clientId}/{messageId} # Server → Client ACK
```
## Migration bestehender Clients
### Phase 1: Server-seitig (✅ Abgeschlossen)
- Message Delivery Layer implementiert
- MqttPublisher nutzt jetzt MessageDeliveryService
- Backward-kompatibel: Legacy-Nachrichten werden weiterhin verarbeitet
### Phase 2: Client-seitig (TODO)
Flutter-App muss angepasst werden:
1. **Envelope-Handling**
```dart
class MessageEnvelope {
String messageId;
DateTime timestamp;
String topic;
Map<String, dynamic> payload;
bool requiresAck;
}
```
2. **Empfang**
```dart
void handleIncomingMessage(String topic, String json) {
final envelope = MessageEnvelope.fromJson(json);
// ACK senden
if (envelope.requiresAck) {
sendAcknowledgment(envelope.messageId);
}
// Payload verarbeiten
processPayload(envelope.payload);
}
```
3. **ACK senden**
```dart
void sendAcknowledgment(String messageId) {
final ack = {
'messageId': messageId,
'status': 'RECEIVED',
'timestamp': DateTime.now().toIso8601String(),
'clientId': myClientId
};
mqtt.publish('/ack/server/$messageId', jsonEncode(ack));
}
```
4. **Senden**
```dart
void sendMessage(String topic, Map<String, dynamic> payload) {
final envelope = MessageEnvelope(
messageId: Uuid().v4(),
timestamp: DateTime.now(),
topic: topic,
payload: payload,
requiresAck: true
);
mqtt.publish(topic, jsonEncode(envelope.toJson()));
}
```
## Monitoring & Debugging
### Logging
Alle Komponenten loggen mit Präfix:
- `[MessageDelivery]`: MessageDeliveryService
- `[MqttTransport]`: MqttTransportAdapter
- `[AckHandler]`: AcknowledgmentHandler
- `[RetryScheduler]`: RetryScheduler
- `[MessagingConfig]`: MessagingConfig
### Datenbank-Queries
**Ausstehende Zustellungen:**
```javascript
db.pending_deliveries.find({ status: "SENT" })
```
**Fehlgeschlagene Zustellungen:**
```javascript
db.pending_deliveries.find({ status: "FAILED" })
```
**Retry-Statistiken:**
```javascript
db.pending_deliveries.aggregate([
{ $group: { _id: "$status", count: { $sum: 1 } } }
])
```
**Nachrichten eines Clients:**
```javascript
db.pending_deliveries.find({ client_id: "client123" })
```
## Vorteile
### Zuverlässigkeit
- Garantierte Zustellung durch Retry-Mechanismus
- Persistierung in MongoDB verhindert Datenverlust
- Acknowledgment-Tracking für Nachvollziehbarkeit
### Skalierbarkeit
- Queue-basiertes Design
- Asynchrone Verarbeitung mit CompletableFuture
- Scheduled Tasks für Hintergrundverarbeitung
### Wartbarkeit
- Klare Trennung der Verantwortlichkeiten
- Protokoll-Abstraktion ermöglicht einfachen Austausch
- Umfangreiches Logging für Debugging
### Flexibilität
- Konfigurierbare Retry-Parameter
- Verschiedene DeliveryOptions (standard, fireAndForget, critical)
- Erweiterbar für andere Transporte (WebSocket, gRPC, etc.)
## Nächste Schritte
1. ✅ Server-seitige Implementierung abgeschlossen
2. ⏳ Flutter-App anpassen (Envelope-Handling)
3. ⏳ Integration-Tests schreiben
4. ⏳ Monitoring-Dashboard erstellen
5. ⏳ Performance-Optimierung
6. ⏳ Dokumentation für Client-Entwickler
## Support
Bei Fragen oder Problemen:
- Logs prüfen: `logs/votianlt.log`
- MongoDB Collections: `message_envelopes`, `pending_deliveries`
- Konfiguration: `application.properties`

View File

@@ -1,266 +0,0 @@
# MQTT Migration Summary - Dokumentationsübersicht
## 📚 Erstellte Dokumentation
Alle Änderungen am MQTT-System wurden umfassend dokumentiert, um die Client-Migration zu erleichtern.
### 1. MQTT_MIGRATION_GUIDE.md
**Zielgruppe**: Client-Entwickler (Flutter/Dart)
**Inhalt**:
- Übersicht aller Server-Änderungen
- Detaillierte Client-Anpassungen mit Code-Beispielen
- Message-Envelope-Format
- Acknowledgment-Handling
- Migration Checklist
- Abwärtskompatibilität
- Vorteile des neuen Systems
**Wichtigste Punkte**:
- ⚠️ Port-Änderung: 1883 → 42099
- ⚠️ Authentifizierung erforderlich: app/apppwd
- ⚠️ Timeout erhöht: 30s → 60s
- ✅ Message-Envelope-Format implementieren
- ✅ ACK-Handling implementieren
### 2. MQTT_QUICK_REFERENCE.md
**Zielgruppe**: Client-Entwickler (Flutter/Dart)
**Inhalt**:
- Kritische Änderungen auf einen Blick
- Fertige Code-Snippets für Flutter/Dart
- Verbindungsaufbau
- Message-Envelope-Klasse
- Nachricht senden/empfangen
- ACK-Handling
- Debugging-Tipps
- Häufige Fehler und Lösungen
**Verwendung**:
Schnelle Referenz während der Implementierung - alle wichtigen Code-Beispiele sofort verfügbar.
### 3. CHANGELOG_MQTT.md
**Zielgruppe**: Alle Entwickler, DevOps
**Inhalt**:
- Breaking Changes
- Neue Features
- Entfernte Komponenten
- Geänderte Komponenten
- Architektur-Änderungen
- Migration Guide
- Kompatibilität
- Testing
- Performance
- Rollback-Plan
**Verwendung**:
Vollständige Historie aller Änderungen für Audit und Troubleshooting.
### 4. MQTT_README.md (aktualisiert)
**Zielgruppe**: Alle Entwickler
**Inhalt**:
- ⚠️ Neue Broker-Konfiguration prominent hervorgehoben
- Aktualisierte Connection-Parameter
- Hinweise auf Migration-Dokumentation
- Bestehende API-Dokumentation bleibt erhalten
**Änderungen**:
- Broker-URL und Port aktualisiert
- Authentifizierung dokumentiert
- Verweise auf neue Dokumentation hinzugefügt
### 5. MESSAGING_LAYER.md (bereits vorhanden)
**Zielgruppe**: Backend-Entwickler
**Inhalt**:
- Detaillierte Architektur des Messaging-Systems
- Plugin-basiertes Design
- Message-Delivery-Service
- Komponenten-Beschreibungen
**Status**: Bereits vorhanden, keine Änderungen erforderlich.
## 🔄 Server-Änderungen (bereits durchgeführt)
### Konfiguration
**Datei**: `src/main/resources/application.properties`
**Entfernt** (Zeilen 57-71):
```properties
# MQTT v5 settings (alt)
app.mqtt.enabled=true
app.mqtt.broker-uri=mqtt://mqtt-2.assecutor.de
app.mqtt.client-id=server-${random.uuid}
# ... weitere alte Einstellungen
```
**Aktiv**:
```properties
# Messaging Plugin Configuration
app.messaging.plugin.type=mqtt
app.messaging.plugin.mqtt.broker.host=mqtt-2.assecutor.de
app.messaging.plugin.mqtt.broker.port=42099
app.messaging.plugin.mqtt.username=app
app.messaging.plugin.mqtt.password=apppwd
app.messaging.plugin.mqtt.client.id=votianlt-server
```
### Code-Änderungen
#### 1. MqttMessagingPlugin.java
**Datei**: `src/main/java/de/assecutor/votianlt/messaging/plugin/mqtt/MqttMessagingPlugin.java`
**Änderungen**:
- Connection-Timeout: 30s → 60s (konfigurierbar)
- Keep-Alive: konfigurierbar (Standard: 60s)
- Verbesserte Fehlerbehandlung:
- TimeoutException → "Broker nicht erreichbar"
- UnknownHostException → "DNS-Fehler"
- ConnectException → "Port blockiert"
- Detailliertes Logging
#### 2. Gelöschte Klassen
- `src/main/java/de/assecutor/votianlt/config/MqttProperties.java`
- `src/main/java/de/assecutor/votianlt/config/MqttConfig.java`
**Grund**: Ersetzt durch Plugin-Konfiguration, nicht mehr verwendet.
## ✅ Verifikation
### Build erfolgreich
```bash
./mvnw clean compile
# [INFO] BUILD SUCCESS
```
### Server läuft
```bash
./mvnw spring-boot:run
# Application running at http://localhost:8080/
```
### MQTT-Verbindung erfolgreich
```
[MqttPlugin] Connected successfully - connAck: MqttConnAck{reasonCode=SUCCESS}
[MqttPlugin] Successfully subscribed to: /ack/server/+
[MqttPlugin] Successfully subscribed to: /server/+/task_completed
[MqttPlugin] Successfully subscribed to: /server/+/jobs/assigned
[MqttPlugin] Successfully subscribed to: /server/+/message
[MqttPlugin] Successfully subscribed to: /server/+/login
```
## 📋 Client Migration Checklist
### Vorbereitung
- [ ] `MQTT_MIGRATION_GUIDE.md` lesen
- [ ] `MQTT_QUICK_REFERENCE.md` als Referenz bereithalten
- [ ] Testumgebung vorbereiten
### Implementierung
- [ ] Port auf 42099 ändern
- [ ] Authentifizierung hinzufügen (app/apppwd)
- [ ] Connection-Timeout auf 60s erhöhen
- [ ] Keep-Alive auf 60s setzen
- [ ] MessageEnvelope-Klasse implementieren (siehe Quick Reference)
- [ ] AcknowledgmentMessage-Klasse implementieren (siehe Quick Reference)
- [ ] Envelope-basiertes Senden implementieren
- [ ] Envelope-basiertes Empfangen implementieren
- [ ] ACK-Handling implementieren
### Testing
- [ ] Verbindung testen (siehe Quick Reference: testConnection())
- [ ] Nachricht senden testen
- [ ] Nachricht empfangen testen
- [ ] ACK-Handling testen
- [ ] Fehlerszenarien testen (Timeout, Verbindungsabbruch)
### Deployment
- [ ] Staging-Umgebung testen
- [ ] Produktions-Deployment planen
- [ ] Rollback-Plan bereithalten
## 🔗 Dokumentations-Struktur
```
MQTT-Dokumentation/
├── MQTT_MIGRATION_GUIDE.md ← Hauptdokumentation für Client-Migration
├── MQTT_QUICK_REFERENCE.md ← Code-Beispiele und Schnellreferenz
├── CHANGELOG_MQTT.md ← Vollständige Änderungshistorie
├── MQTT_README.md ← API-Referenz (aktualisiert)
├── MESSAGING_LAYER.md ← Architektur-Dokumentation (vorhanden)
└── MIGRATION_SUMMARY.md ← Diese Datei (Übersicht)
```
## 🎯 Nächste Schritte
### Für Client-Entwickler:
1. **Lesen**: `MQTT_MIGRATION_GUIDE.md` durcharbeiten
2. **Referenz**: `MQTT_QUICK_REFERENCE.md` für Code-Beispiele nutzen
3. **Implementieren**: Änderungen gemäß Checklist umsetzen
4. **Testen**: Verbindung und Nachrichtenaustausch testen
5. **Deployen**: Nach erfolgreichem Test in Produktion bringen
### Für Backend-Entwickler:
1. **Monitoring**: Message-Delivery-Metriken überwachen
2. **Logs**: Verbindungsprobleme in Logs prüfen
3. **Support**: Client-Entwickler bei Migration unterstützen
4. **Dokumentation**: Bei Bedarf weitere Beispiele hinzufügen
### Für DevOps:
1. **Firewall**: Port 42099 für Clients freigeben
2. **Monitoring**: MQTT-Broker-Status überwachen
3. **Backup**: Rollback-Plan bereithalten
4. **Logs**: Zentrales Logging für MQTT-Verbindungen einrichten
## 💡 Wichtige Hinweise
### Abwärtskompatibilität
Der Server unterstützt **vorübergehend** noch Legacy-Nachrichten ohne Envelope-Format. Dies ermöglicht eine schrittweise Migration der Clients.
**Empfehlung**: Alle Clients sollten so schnell wie möglich auf das neue Envelope-Format umgestellt werden.
### Vorteile des neuen Systems
1. **Zuverlässigkeit**: ACK-basierte Zustellung mit Retries
2. **Nachverfolgung**: Jede Nachricht hat eindeutige ID
3. **Fehlerbehandlung**: Detaillierte Fehlerinformationen
4. **Monitoring**: Vollständige Nachrichtenverfolgung
5. **Skalierbarkeit**: Queue-basiertes Design
### Support
Bei Fragen oder Problemen:
1. Dokumentation konsultieren (siehe oben)
2. Logs auf Client- und Server-Seite prüfen
3. Netzwerk-Konnektivität testen (Port 42099)
4. MQTT-Client-Tool verwenden (z.B. MQTT Explorer)
## 📊 Zusammenfassung
### Was wurde geändert?
- ✅ MQTT-Broker-Port: 1883 → 42099
- ✅ Authentifizierung hinzugefügt: app/apppwd
- ✅ Timeout erhöht: 30s → 60s
- ✅ Alte Konfiguration entfernt
- ✅ Fehlerbehandlung verbessert
### Was wurde dokumentiert?
- ✅ Migration Guide für Clients
- ✅ Quick Reference mit Code-Beispielen
- ✅ Vollständiger Changelog
- ✅ Aktualisierte API-Dokumentation
- ✅ Diese Übersicht
### Was muss der Client tun?
- ⚠️ Port auf 42099 ändern
- ⚠️ Authentifizierung hinzufügen
- ⚠️ Timeout erhöhen
- ✅ Message-Envelope implementieren (optional, aber empfohlen)
- ✅ ACK-Handling implementieren (optional, aber empfohlen)
### Zeitplan
- **Sofort**: Port und Authentifizierung ändern (kritisch!)
- **Kurzfristig**: Envelope-Format implementieren (empfohlen)
- **Mittelfristig**: ACK-Handling implementieren (empfohlen)
---
**Stand**: 2025-10-22
**Version**: 2.0.0
**Status**: ✅ Server-Migration abgeschlossen, Client-Migration ausstehend

View File

@@ -1,282 +0,0 @@
# MQTT System Dokumentation - Übersicht
## 🎯 Zweck dieser Dokumentation
Diese Dokumentation beschreibt die Migration des MQTT-Systems auf Version 2.0 mit Plugin-basierter Architektur und hilft Client-Entwicklern bei der Umstellung ihrer Anwendungen.
## 📚 Dokumentations-Struktur
### Für Client-Entwickler (Flutter/Dart)
#### 1. **MQTT_QUICK_REFERENCE.md** ⭐ START HERE
**Empfohlen für**: Schnelle Implementierung
**Inhalt**:
- ⚠️ Kritische Änderungen auf einen Blick
- 📋 Fertige Code-Snippets zum Copy-Paste
- 🔧 Verbindungsaufbau
- 📨 Nachricht senden/empfangen
- ✅ ACK-Handling
- 🐛 Debugging-Tipps
- ❌ Häufige Fehler und Lösungen
**Verwendung**: Als Referenz während der Implementierung offen halten.
#### 2. **MQTT_MIGRATION_GUIDE.md** 📖 DETAILLIERT
**Empfohlen für**: Vollständiges Verständnis
**Inhalt**:
- 📊 Übersicht aller Server-Änderungen
- 🔄 Detaillierte Client-Anpassungen
- 📦 Message-Envelope-Format
- ✅ Acknowledgment-System
- ☑️ Migration Checklist
- 🔙 Abwärtskompatibilität
- 💡 Vorteile des neuen Systems
- 🧪 Testing-Strategien
**Verwendung**: Vor der Implementierung komplett durchlesen.
### Für alle Entwickler
#### 3. **MIGRATION_SUMMARY.md** 📋 ÜBERSICHT
**Empfohlen für**: Schneller Überblick
**Inhalt**:
- 📚 Übersicht aller Dokumentationen
- 🔄 Zusammenfassung der Server-Änderungen
- ✅ Verifikation der Änderungen
- 📋 Client Migration Checklist
- 🔗 Dokumentations-Struktur
- 🎯 Nächste Schritte
**Verwendung**: Als Einstiegspunkt und Orientierung.
#### 4. **CHANGELOG_MQTT.md** 📝 HISTORIE
**Empfohlen für**: Audit und Troubleshooting
**Inhalt**:
- 💥 Breaking Changes
- ✨ Neue Features
- 🗑️ Entfernte Komponenten
- 🔧 Geänderte Komponenten
- 🏗️ Architektur-Änderungen
- 🧪 Testing-Ergebnisse
- 📊 Performance-Metriken
- 🔄 Rollback-Plan
**Verwendung**: Für detaillierte Änderungshistorie und Rollback-Planung.
#### 5. **MQTT_README.md** 📖 API-REFERENZ
**Empfohlen für**: API-Dokumentation
**Inhalt**:
- 🔌 Broker-Konfiguration (aktualisiert!)
- 📡 Topic-Struktur
- 📨 Message-Formate
- 🔐 Authentifizierung
- ⚙️ Connection-Parameter
**Verwendung**: Als API-Referenz für Topic-Struktur und Message-Formate.
### Für Backend-Entwickler
#### 6. **MESSAGING_LAYER.md** 🏗️ ARCHITEKTUR
**Empfohlen für**: Backend-Entwicklung
**Inhalt**:
- 🏗️ Architektur-Übersicht
- 🔌 Plugin-System
- 📦 Message-Delivery-Service
- 🔄 Nachrichtenfluss
- 🧩 Komponenten-Beschreibungen
- 🔧 Konfiguration
- 🐛 Fehlerbehandlung
**Verwendung**: Für Backend-Entwicklung und Architektur-Verständnis.
## 🚀 Quick Start für Client-Entwickler
### Schritt 1: Kritische Änderungen verstehen
```bash
# Lesen Sie zuerst die kritischen Änderungen
cat MQTT_QUICK_REFERENCE.md | head -50
```
**Wichtigste Änderungen**:
- ⚠️ **Port**: 1883 → **42099**
- ⚠️ **Auth**: Username: `app`, Password: `apppwd`
- ⚠️ **Timeout**: 30s → **60s**
### Schritt 2: Code-Beispiele kopieren
```bash
# Öffnen Sie die Quick Reference
open MQTT_QUICK_REFERENCE.md
```
Kopieren Sie die Code-Snippets für:
1. Verbindungsaufbau
2. MessageEnvelope-Klasse
3. Nachricht senden
4. Nachricht empfangen
5. ACK-Handling
### Schritt 3: Implementieren und Testen
```dart
// 1. Verbindung testen
await testConnection();
// 2. Nachricht senden testen
await sendMessage(client, clientId, 'test', {'hello': 'world'});
// 3. Nachricht empfangen testen
setupMessageListener(client, clientId);
```
### Schritt 4: Migration Checklist abarbeiten
```bash
# Öffnen Sie die Migration Checklist
open MIGRATION_SUMMARY.md
```
Arbeiten Sie die Checklist Punkt für Punkt ab.
## 📊 Änderungen auf einen Blick
### Server-Änderungen (✅ Abgeschlossen)
| Komponente | Alt | Neu | Status |
|------------|-----|-----|--------|
| **Port** | 1883 | 42099 | ✅ |
| **Auth** | Keine | app/apppwd | ✅ |
| **Timeout** | 30s | 60s | ✅ |
| **Konfiguration** | app.mqtt.* | app.messaging.plugin.mqtt.* | ✅ |
| **Fehlerbehandlung** | Basic | Detailliert | ✅ |
### Client-Änderungen (⚠️ Erforderlich)
| Aufgabe | Priorität | Dokumentation |
|---------|-----------|---------------|
| Port auf 42099 ändern | 🔴 KRITISCH | Quick Reference |
| Authentifizierung hinzufügen | 🔴 KRITISCH | Quick Reference |
| Timeout auf 60s erhöhen | 🟡 WICHTIG | Quick Reference |
| MessageEnvelope implementieren | 🟢 EMPFOHLEN | Migration Guide |
| ACK-Handling implementieren | 🟢 EMPFOHLEN | Migration Guide |
## 🎯 Empfohlener Workflow
### Für Client-Entwickler
```mermaid
graph LR
A[Start] --> B[Quick Reference lesen]
B --> C[Kritische Änderungen umsetzen]
C --> D[Code-Snippets kopieren]
D --> E[Implementieren]
E --> F[Testen]
F --> G{Tests OK?}
G -->|Nein| H[Migration Guide konsultieren]
H --> E
G -->|Ja| I[Deployment]
I --> J[Ende]
```
### Für Backend-Entwickler
```mermaid
graph LR
A[Start] --> B[Changelog lesen]
B --> C[Messaging Layer verstehen]
C --> D[Monitoring einrichten]
D --> E[Client-Support]
E --> F[Ende]
```
## 🔍 Troubleshooting-Guide
### Problem: Wo finde ich...?
| Frage | Antwort | Dokument |
|-------|---------|----------|
| Wie verbinde ich mich? | Code-Snippet | MQTT_QUICK_REFERENCE.md |
| Warum funktioniert die Verbindung nicht? | Debugging-Tipps | MQTT_QUICK_REFERENCE.md |
| Was ist ein MessageEnvelope? | Detaillierte Erklärung | MQTT_MIGRATION_GUIDE.md |
| Welche Änderungen gab es? | Vollständige Liste | CHANGELOG_MQTT.md |
| Wie ist die Architektur? | Architektur-Diagramm | MESSAGING_LAYER.md |
| Was sind die Topics? | Topic-Struktur | MQTT_README.md |
### Problem: Verbindung schlägt fehl
```bash
# 1. Quick Reference öffnen
open MQTT_QUICK_REFERENCE.md
# 2. Abschnitt "Debugging" suchen
# 3. "Verbindung testen" Code ausführen
# 4. "Häufige Fehler" konsultieren
```
### Problem: Nachrichten kommen nicht an
```bash
# 1. Migration Guide öffnen
open MQTT_MIGRATION_GUIDE.md
# 2. Abschnitt "Nachrichten empfangen" suchen
# 3. Code-Beispiel mit eigenem Code vergleichen
# 4. Topic-Struktur in MQTT_README.md prüfen
```
## 📞 Support
### Dokumentation nicht hilfreich?
1. **Logs prüfen**: Client- und Server-Logs analysieren
2. **Netzwerk testen**: Port 42099 Erreichbarkeit prüfen
3. **MQTT-Tool verwenden**: MQTT Explorer zum Debuggen nutzen
4. **Changelog konsultieren**: Alle Änderungen nochmal durchgehen
### Weitere Ressourcen
- **HiveMQ Dokumentation**: https://www.hivemq.com/docs/
- **MQTT v5 Spezifikation**: https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html
- **Flutter MQTT Client**: https://pub.dev/packages/mqtt_client
## 📈 Status
| Komponente | Status | Datum |
|------------|--------|-------|
| Server-Migration | ✅ Abgeschlossen | 2025-10-22 |
| Dokumentation | ✅ Abgeschlossen | 2025-10-22 |
| Client-Migration | ⏳ Ausstehend | TBD |
| Testing | ⏳ Ausstehend | TBD |
| Deployment | ⏳ Ausstehend | TBD |
## 🎓 Lernpfad
### Anfänger (Nur Verbindung herstellen)
1. MQTT_QUICK_REFERENCE.md → Abschnitt "Verbindung herstellen"
2. Code kopieren und anpassen
3. Testen
### Fortgeschritten (Vollständige Integration)
1. MIGRATION_SUMMARY.md → Überblick verschaffen
2. MQTT_MIGRATION_GUIDE.md → Komplett durchlesen
3. MQTT_QUICK_REFERENCE.md → Als Referenz nutzen
4. Implementieren und testen
### Experte (Architektur verstehen)
1. CHANGELOG_MQTT.md → Alle Änderungen verstehen
2. MESSAGING_LAYER.md → Architektur studieren
3. MQTT_README.md → API-Details lernen
4. Eigene Erweiterungen entwickeln
## 📝 Feedback
Diese Dokumentation wurde erstellt, um die Client-Migration so einfach wie möglich zu machen. Bei Fragen, Unklarheiten oder Verbesserungsvorschlägen:
1. Dokumentation aktualisieren
2. Beispiele hinzufügen
3. Diagramme erweitern
---
**Version**: 2.0.0
**Stand**: 2025-10-22
**Autor**: System-Migration
**Status**: ✅ Vollständig

View File

@@ -1,398 +0,0 @@
# MQTT Migration Guide - Client Implementation
## Übersicht
Dieses Dokument beschreibt die Änderungen am MQTT-Messaging-System und wie mobile Clients (Flutter/Dart) auf den neuen Messaging-Layer umgestellt werden müssen.
## Änderungen am Server
### 1. Neue MQTT-Broker-Konfiguration
**Wichtig: Der MQTT-Broker-Port hat sich geändert!**
```
Alter Port: 1883
Neuer Port: 42099
Broker: mqtt-2.assecutor.de:42099
```
### 2. Server-Konfiguration
Die Server-Konfiguration wurde von der alten `app.mqtt.*` Konfiguration auf das neue Plugin-System umgestellt:
**Alte Konfiguration (nicht mehr verwendet):**
```properties
app.mqtt.enabled=true
app.mqtt.broker-uri=mqtt://mqtt-2.assecutor.de
app.mqtt.client-id=server-${random.uuid}
```
**Neue Konfiguration:**
```properties
# Messaging Plugin Configuration
app.messaging.plugin.type=mqtt
app.messaging.plugin.mqtt.broker.host=mqtt-2.assecutor.de
app.messaging.plugin.mqtt.broker.port=42099
app.messaging.plugin.mqtt.username=app
app.messaging.plugin.mqtt.password=apppwd
app.messaging.plugin.mqtt.client.id=votianlt-server
```
### 3. Verbesserte Fehlerbehandlung
Der Server hat jetzt:
- Erhöhten Connection-Timeout (60 Sekunden statt 30)
- Detaillierte Fehlerdiagnose für Verbindungsprobleme
- Automatische Wiederverbindung mit exponentieller Backoff-Strategie
## Client-Anpassungen erforderlich
### 1. MQTT-Broker-Verbindung aktualisieren
**Flutter/Dart Beispiel:**
```dart
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
class MqttService {
// WICHTIG: Neuer Port!
static const String BROKER_HOST = 'mqtt-2.assecutor.de';
static const int BROKER_PORT = 42099; // Geändert von 1883
static const String USERNAME = 'app';
static const String PASSWORD = 'apppwd';
late MqttServerClient client;
Future<void> connect(String clientId) async {
client = MqttServerClient.withPort(
BROKER_HOST,
clientId,
BROKER_PORT, // Neuer Port
);
client.logging(on: true);
client.keepAlivePeriod = 60;
client.connectTimeoutPeriod = 60000; // 60 Sekunden
client.autoReconnect = true;
// Authentifizierung
client.setProtocolV311();
final connMessage = MqttConnectMessage()
.withClientIdentifier(clientId)
.authenticateAs(USERNAME, PASSWORD)
.withWillQos(MqttQos.atLeastOnce)
.startClean()
.keepAliveFor(60);
client.connectionMessage = connMessage;
try {
await client.connect();
print('MQTT Connected successfully');
} catch (e) {
print('MQTT Connection failed: $e');
client.disconnect();
rethrow;
}
}
}
```
### 2. Topic-Struktur (unverändert)
Die Topic-Struktur bleibt gleich:
**Client → Server:**
```
/server/{clientId}/{messageType}
```
**Server → Client:**
```
/client/{clientId}/{messageType}
```
**Acknowledgments:**
```
Client → Server: /ack/server/{messageId}
Server → Client: /ack/client/{clientId}/{messageId}
```
### 3. Message Envelope Format
Der Server verwendet jetzt ein Message-Envelope-Format für alle Nachrichten:
```json
{
"messageId": "uuid-v4",
"timestamp": "2025-10-22T10:30:00Z",
"topic": "/client/app-user-123/jobs/assigned",
"payload": {
// Ihre eigentliche Nachricht
},
"requiresAck": true,
"retryCount": 0,
"expiresAt": "2025-10-23T10:30:00Z"
}
```
**Client-Implementierung:**
```dart
class MessageEnvelope {
final String messageId;
final DateTime timestamp;
final String topic;
final Map<String, dynamic> payload;
final bool requiresAck;
final int retryCount;
final DateTime? expiresAt;
MessageEnvelope({
required this.messageId,
required this.timestamp,
required this.topic,
required this.payload,
this.requiresAck = true,
this.retryCount = 0,
this.expiresAt,
});
factory MessageEnvelope.fromJson(Map<String, dynamic> json) {
return MessageEnvelope(
messageId: json['messageId'],
timestamp: DateTime.parse(json['timestamp']),
topic: json['topic'],
payload: json['payload'],
requiresAck: json['requiresAck'] ?? true,
retryCount: json['retryCount'] ?? 0,
expiresAt: json['expiresAt'] != null
? DateTime.parse(json['expiresAt'])
: null,
);
}
Map<String, dynamic> toJson() {
return {
'messageId': messageId,
'timestamp': timestamp.toIso8601String(),
'topic': topic,
'payload': payload,
'requiresAck': requiresAck,
'retryCount': retryCount,
'expiresAt': expiresAt?.toIso8601String(),
};
}
}
```
### 4. Acknowledgment-Handling
Wenn eine Nachricht mit `requiresAck: true` empfangen wird, muss der Client eine Bestätigung senden:
```dart
class AcknowledgmentMessage {
final String messageId;
final DateTime timestamp;
final String status; // "SUCCESS" oder "FAILED"
final String? errorMessage;
AcknowledgmentMessage({
required this.messageId,
required this.timestamp,
required this.status,
this.errorMessage,
});
Map<String, dynamic> toJson() {
return {
'messageId': messageId,
'timestamp': timestamp.toIso8601String(),
'status': status,
if (errorMessage != null) 'errorMessage': errorMessage,
};
}
}
// Verwendung:
void sendAcknowledgment(String messageId, bool success, [String? error]) {
final ack = AcknowledgmentMessage(
messageId: messageId,
timestamp: DateTime.now(),
status: success ? 'SUCCESS' : 'FAILED',
errorMessage: error,
);
final topic = '/ack/server/$messageId';
final payload = jsonEncode(ack.toJson());
final builder = MqttClientPayloadBuilder();
builder.addString(payload);
client.publishMessage(
topic,
MqttQos.exactlyOnce,
builder.payload!,
);
}
```
### 5. Nachrichten empfangen und verarbeiten
```dart
void setupMessageHandlers() {
client.updates!.listen((List<MqttReceivedMessage<MqttMessage>> messages) {
for (var message in messages) {
final topic = message.topic;
final payload = MqttPublishPayload.bytesToStringAsString(
(message.payload as MqttPublishMessage).payload.message,
);
try {
final json = jsonDecode(payload);
// Prüfen, ob es ein Envelope ist
if (json.containsKey('messageId') && json.containsKey('payload')) {
final envelope = MessageEnvelope.fromJson(json);
// Nachricht verarbeiten
handleMessage(envelope);
// ACK senden, wenn erforderlich
if (envelope.requiresAck) {
sendAcknowledgment(envelope.messageId, true);
}
} else {
// Legacy-Nachricht ohne Envelope
handleLegacyMessage(topic, json);
}
} catch (e) {
print('Error processing message: $e');
// Bei Envelope-Nachrichten: Fehler-ACK senden
if (json.containsKey('messageId')) {
sendAcknowledgment(json['messageId'], false, e.toString());
}
}
}
});
}
```
### 6. Nachrichten senden
```dart
Future<void> sendMessage(
String messageType,
Map<String, dynamic> payload,
{bool requiresAck = true}
) async {
final envelope = MessageEnvelope(
messageId: Uuid().v4(),
timestamp: DateTime.now(),
topic: '/server/$clientId/$messageType',
payload: payload,
requiresAck: requiresAck,
);
final topic = envelope.topic;
final message = jsonEncode(envelope.toJson());
final builder = MqttClientPayloadBuilder();
builder.addString(message);
client.publishMessage(
topic,
MqttQos.exactlyOnce,
builder.payload!,
);
print('Message sent: $messageType');
}
```
## Migration Checklist für Clients
- [ ] **MQTT-Broker-Port auf 42099 ändern**
- [ ] **Connection-Timeout auf mindestens 60 Sekunden erhöhen**
- [ ] **Keep-Alive auf 60 Sekunden setzen**
- [ ] **Authentifizierung hinzufügen** (username: `app`, password: `apppwd`)
- [ ] **MessageEnvelope-Klasse implementieren**
- [ ] **AcknowledgmentMessage-Klasse implementieren**
- [ ] **Envelope-basierte Nachrichtenverarbeitung implementieren**
- [ ] **ACK-Handling für eingehende Nachrichten implementieren**
- [ ] **Ausgehende Nachrichten in Envelopes verpacken**
- [ ] **Fehlerbehandlung für abgelaufene Nachrichten implementieren**
- [ ] **Retry-Logik für fehlgeschlagene Nachrichten implementieren**
## Abwärtskompatibilität
Der Server unterstützt derzeit noch Legacy-Nachrichten ohne Envelope-Format, aber es wird empfohlen, so schnell wie möglich auf das neue Format umzustellen.
**Legacy-Format (wird noch unterstützt):**
```json
{
"taskId": "123",
"status": "completed"
}
```
**Neues Format (empfohlen):**
```json
{
"messageId": "uuid",
"timestamp": "2025-10-22T10:30:00Z",
"topic": "/server/client-123/task_completed",
"payload": {
"taskId": "123",
"status": "completed"
},
"requiresAck": true
}
```
## Vorteile des neuen Systems
1. **Zuverlässige Zustellung**: ACK-basiertes System mit automatischen Wiederholungen
2. **Nachrichtenverfolgung**: Jede Nachricht hat eine eindeutige ID
3. **Ablaufverwaltung**: Nachrichten können ablaufen und werden automatisch bereinigt
4. **Bessere Fehlerbehandlung**: Detaillierte Fehlerinformationen
5. **Monitoring**: Vollständige Nachrichtenverfolgung im System
## Testen der Verbindung
```dart
Future<void> testConnection() async {
try {
final mqttService = MqttService();
await mqttService.connect('test-client-${Uuid().v4()}');
// Test-Nachricht senden
await mqttService.sendMessage(
'test',
{'message': 'Hello from client'},
);
print('Connection test successful!');
} catch (e) {
print('Connection test failed: $e');
}
}
```
## Support und Fragen
Bei Fragen oder Problemen während der Migration:
1. Prüfen Sie die Logs auf beiden Seiten (Client und Server)
2. Stellen Sie sicher, dass der Port 42099 erreichbar ist
3. Überprüfen Sie die Authentifizierungsdaten
4. Testen Sie die Verbindung mit einem MQTT-Client-Tool (z.B. MQTT Explorer)
## Weitere Dokumentation
- `MESSAGING_LAYER.md` - Detaillierte Architektur des Messaging-Systems
- `MQTT_README.md` - MQTT-API-Dokumentation
- `CLAUDE.md` - Allgemeine Systemarchitektur

View File

@@ -1,367 +0,0 @@
# MQTT Quick Reference - Client Migration
## 🚨 Kritische Änderungen
### Port-Änderung (WICHTIG!)
```
Alt: mqtt-2.assecutor.de:1883
Neu: mqtt-2.assecutor.de:42099
```
### Authentifizierung (NEU!)
```
Username: app
Password: apppwd
```
### Timeout-Erhöhung
```
Connection Timeout: 60 Sekunden (vorher 30s)
Keep-Alive: 60 Sekunden (vorher 30s)
```
## 📋 Flutter/Dart Code-Snippets
### 1. Verbindung herstellen
```dart
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
Future<MqttServerClient> connectToMqtt(String clientId) async {
final client = MqttServerClient.withPort(
'mqtt-2.assecutor.de',
clientId,
42099, // NEUER PORT!
);
client.keepAlivePeriod = 60;
client.connectTimeoutPeriod = 60000;
client.autoReconnect = true;
client.setProtocolV311();
final connMessage = MqttConnectMessage()
.withClientIdentifier(clientId)
.authenticateAs('app', 'apppwd') // NEU: Authentifizierung
.withWillQos(MqttQos.atLeastOnce)
.startClean()
.keepAliveFor(60);
client.connectionMessage = connMessage;
await client.connect();
return client;
}
```
### 2. Message Envelope (NEU!)
```dart
class MessageEnvelope {
final String messageId;
final DateTime timestamp;
final String topic;
final Map<String, dynamic> payload;
final bool requiresAck;
final int retryCount;
final DateTime? expiresAt;
MessageEnvelope({
required this.messageId,
required this.timestamp,
required this.topic,
required this.payload,
this.requiresAck = true,
this.retryCount = 0,
this.expiresAt,
});
factory MessageEnvelope.fromJson(Map<String, dynamic> json) {
return MessageEnvelope(
messageId: json['messageId'],
timestamp: DateTime.parse(json['timestamp']),
topic: json['topic'],
payload: json['payload'],
requiresAck: json['requiresAck'] ?? true,
retryCount: json['retryCount'] ?? 0,
expiresAt: json['expiresAt'] != null
? DateTime.parse(json['expiresAt'])
: null,
);
}
Map<String, dynamic> toJson() => {
'messageId': messageId,
'timestamp': timestamp.toIso8601String(),
'topic': topic,
'payload': payload,
'requiresAck': requiresAck,
'retryCount': retryCount,
if (expiresAt != null) 'expiresAt': expiresAt!.toIso8601String(),
};
}
```
### 3. Nachricht senden
```dart
import 'package:uuid/uuid.dart';
import 'dart:convert';
Future<void> sendMessage(
MqttServerClient client,
String clientId,
String messageType,
Map<String, dynamic> payload,
) async {
final envelope = MessageEnvelope(
messageId: Uuid().v4(),
timestamp: DateTime.now(),
topic: '/server/$clientId/$messageType',
payload: payload,
requiresAck: true,
);
final builder = MqttClientPayloadBuilder();
builder.addString(jsonEncode(envelope.toJson()));
client.publishMessage(
envelope.topic,
MqttQos.exactlyOnce,
builder.payload!,
);
}
// Beispiel-Verwendung:
await sendMessage(
client,
'app-user-123',
'task_completed',
{
'taskId': '456',
'status': 'completed',
'completedAt': DateTime.now().toIso8601String(),
},
);
```
### 4. Nachricht empfangen
```dart
void setupMessageListener(MqttServerClient client, String clientId) {
// Subscribe zu allen relevanten Topics
client.subscribe('/client/$clientId/#', MqttQos.exactlyOnce);
client.subscribe('/ack/client/$clientId/#', MqttQos.exactlyOnce);
client.updates!.listen((List<MqttReceivedMessage<MqttMessage>> messages) {
for (var message in messages) {
final topic = message.topic;
final payloadString = MqttPublishPayload.bytesToStringAsString(
(message.payload as MqttPublishMessage).payload.message,
);
try {
final json = jsonDecode(payloadString);
// Prüfen ob Envelope-Format
if (json.containsKey('messageId') && json.containsKey('payload')) {
final envelope = MessageEnvelope.fromJson(json);
// Nachricht verarbeiten
handleEnvelopeMessage(envelope);
// ACK senden wenn erforderlich
if (envelope.requiresAck) {
sendAck(client, envelope.messageId, true);
}
} else {
// Legacy-Format
handleLegacyMessage(topic, json);
}
} catch (e) {
print('Error processing message: $e');
}
}
});
}
```
### 5. Acknowledgment senden (NEU!)
```dart
class AcknowledgmentMessage {
final String messageId;
final DateTime timestamp;
final String status;
final String? errorMessage;
AcknowledgmentMessage({
required this.messageId,
required this.timestamp,
required this.status,
this.errorMessage,
});
Map<String, dynamic> toJson() => {
'messageId': messageId,
'timestamp': timestamp.toIso8601String(),
'status': status,
if (errorMessage != null) 'errorMessage': errorMessage,
};
}
void sendAck(
MqttServerClient client,
String messageId,
bool success,
[String? errorMessage]
) {
final ack = AcknowledgmentMessage(
messageId: messageId,
timestamp: DateTime.now(),
status: success ? 'SUCCESS' : 'FAILED',
errorMessage: errorMessage,
);
final topic = '/ack/server/$messageId';
final builder = MqttClientPayloadBuilder();
builder.addString(jsonEncode(ack.toJson()));
client.publishMessage(
topic,
MqttQos.exactlyOnce,
builder.payload!,
);
}
```
## 📊 Topic-Struktur (unverändert)
### Client → Server
```
/server/{clientId}/task_completed
/server/{clientId}/login
/server/{clientId}/message
/server/{clientId}/jobs/assigned
```
### Server → Client
```
/client/{clientId}/jobs/assigned
/client/{clientId}/message
/client/{clientId}/task_update
```
### Acknowledgments
```
Client → Server: /ack/server/{messageId}
Server → Client: /ack/client/{clientId}/{messageId}
```
## 🔍 Debugging
### Verbindung testen
```dart
Future<void> testConnection() async {
try {
final client = await connectToMqtt('test-${Uuid().v4()}');
if (client.connectionStatus!.state == MqttConnectionState.connected) {
print('✅ Connection successful!');
// Test-Nachricht senden
await sendMessage(
client,
'test-client',
'test',
{'message': 'Hello from client'},
);
print('✅ Test message sent!');
} else {
print('❌ Connection failed: ${client.connectionStatus}');
}
} catch (e) {
print('❌ Error: $e');
}
}
```
### Häufige Fehler
#### 1. Connection Timeout
```
Fehler: TimeoutException after 60 seconds
Lösung:
- Prüfen Sie, ob Port 42099 erreichbar ist
- Firewall-Einstellungen überprüfen
- Netzwerkverbindung testen
```
#### 2. Authentication Failed
```
Fehler: Connection refused
Lösung:
- Username: 'app'
- Password: 'apppwd'
- Stellen Sie sicher, dass authenticateAs() aufgerufen wird
```
#### 3. Message not received
```
Fehler: Keine Nachrichten empfangen
Lösung:
- Subscription überprüfen: client.subscribe('/client/$clientId/#', ...)
- Listener registriert: client.updates!.listen(...)
- Topic-Format korrekt: /client/{clientId}/{messageType}
```
## 📦 Dependencies
### pubspec.yaml
```yaml
dependencies:
mqtt_client: ^10.2.0
uuid: ^4.0.0
```
## ✅ Migration Checklist
- [ ] Port auf 42099 geändert
- [ ] Authentifizierung hinzugefügt (app/apppwd)
- [ ] Connection-Timeout auf 60s erhöht
- [ ] Keep-Alive auf 60s gesetzt
- [ ] MessageEnvelope-Klasse implementiert
- [ ] AcknowledgmentMessage-Klasse implementiert
- [ ] Envelope-basiertes Senden implementiert
- [ ] Envelope-basiertes Empfangen implementiert
- [ ] ACK-Handling implementiert
- [ ] Fehlerbehandlung für ACKs implementiert
- [ ] Verbindung getestet
- [ ] Nachrichtenversand getestet
- [ ] Nachrichtenempfang getestet
## 🔗 Weitere Dokumentation
- **Detaillierte Migration**: `MQTT_MIGRATION_GUIDE.md`
- **Changelog**: `CHANGELOG_MQTT.md`
- **Architektur**: `MESSAGING_LAYER.md`
- **API-Referenz**: `MQTT_README.md`
## 💡 Best Practices
1. **Eindeutige Client-IDs**: Verwenden Sie stabile, geräte-spezifische IDs
2. **Fehlerbehandlung**: Implementieren Sie Retry-Logik für fehlgeschlagene Verbindungen
3. **ACK-Timeout**: Warten Sie maximal 30 Sekunden auf ACKs
4. **Message-Expiry**: Prüfen Sie `expiresAt` vor der Verarbeitung
5. **Logging**: Loggen Sie alle Verbindungsereignisse für Debugging
## 🆘 Support
Bei Problemen:
1. Logs auf Client- und Server-Seite prüfen
2. Netzwerk-Konnektivität testen (Port 42099)
3. MQTT-Client-Tool verwenden (z.B. MQTT Explorer)
4. Dokumentation konsultieren

View File

@@ -1,191 +0,0 @@
# VOTIANLT MQTT Messaging API
This document describes how mobile/Flutter apps should communicate with the backend using MQTT. It replaces the previous STOMP/WebSocket communication.
## ⚠️ WICHTIG: Neue Konfiguration (Stand: 2025-10-22)
**Broker**: `mqtt-2.assecutor.de:42099` (MQTT v5)
**Port**: `42099` (geändert von 1883!)
**QoS**: 2 (exactly once)
**Retain**: Enabled for critical topics (see below), otherwise not retained
**Payloads**: JSON (UTF8)
### Connection Parameters
- **MQTT clientId**: choose a stable, unique per-device id (e.g., app-<uuid>)
- **Clean session**: false (recommended for guaranteed delivery). The broker will queue QoS>0 messages while the app is offline.
- **Authentication**: **REQUIRED** (neu!)
- Username: `app`
- Password: `apppwd`
- **Keep-Alive**: 60 seconds
- **Connection Timeout**: 60 seconds
### Migration Notice
📖 **Für die Migration auf das neue System siehe:**
- `MQTT_MIGRATION_GUIDE.md` - Detaillierte Migrationsanleitung
- `MQTT_QUICK_REFERENCE.md` - Schnellreferenz mit Code-Beispielen
- `CHANGELOG_MQTT.md` - Vollständige Liste aller Änderungen
Topic Naming (v1/*)
- v1/app/<deviceId>/auth/login (App -> Server)
- v1/users/<username>/notifications (Server -> App)
- v1/broadcasts (Server -> App)
- v1/app/<deviceId>/jobs/assigned (App -> Server request)
- v1/app/<deviceId>/job/status (App -> Server)
- v1/app/<deviceId>/device/location (App -> Server)
- v1/tasks/<taskId> (Server -> App events for a single task)
- v1/task-updates (Server -> App general task events) [optional]
General pattern
- Requests from apps go under v1/app/<deviceId>/...
- Server responses and events are published to either a user or taskscoped topic as listed above.
1) Authentication (App -> Server)
Topic: v1/app/<deviceId>/auth/login
Payload request:
{
"email": "user@example.com",
"password": "secret"
}
Response (Server -> App)
Topic: v1/users/<username-or-appUserId>/notifications
Payload:
{
"type": "auth",
"success": true,
"message": "Anmeldung erfolgreich",
"appUserId": "<ObjectId>"
}
2) Job status update (App -> Server)
Topic: v1/app/<deviceId>/job/status
Payload request (example):
{
"jobId": "<ObjectId>",
"status": "ON_ROUTE",
"note": "...",
"timestamp": "2025-09-13T22:00:00"
}
Server may publish derived updates to:
- v1/broadcasts (if global) or
- v1/users/<username>/notifications (if per user)
3) Device location (App -> Server)
Topic: v1/app/<deviceId>/device/location
Payload:
{
"lat": 48.12345,
"lon": 11.54321,
"accuracy": 5.4,
"timestamp": "2025-09-13T22:00:00"
}
4) Assigned jobs request (App -> Server)
Topic: v1/app/<deviceId>/jobs/assigned
Payload request:
{
"appUserId": "<ObjectId>"
}
Response (Server -> App)
Topic: v1/users/<appUserId>/notifications
Payload:
{
"type": "jobs",
"jobs": [ { /* JobWithRelatedDataDTO */ } ]
}
5) Task completion events (Server -> App)
- When a task is completed (CONFIRMATION, SIGNATURE, BARCODE, TODOLIST, PHOTO), the server publishes an event to the taskscoped topic.
Topic: v1/tasks/<taskId>
Payload:
{
"event": "taskCompleted",
"taskId": "<taskId>",
"jobId": "<jobId>",
"taskType": "PHOTO|CONFIRMATION|...",
"completed": true,
"completedAt": "2025-09-13T22:05:00",
"completedBy": "driver01",
"note": "optional"
}
6) Photo uploads (MQTT)
- Apps send photos as base64 strings within the MQTT payload when reporting PHOTO task completion.
Topic (App -> Server): v1/app/<deviceId>/task/photo/completed
Payload:
{
"taskId": "<taskId>",
"completedBy": "driver01",
"note": "optional",
"extraData": {
"photos": ["<base64-1>", "<base64-2>"],
"count": 2
}
}
Server behavior:
- Saves the photos in the photos collection, marks the task as completed, and publishes an event:
Topic (Server -> App): v1/tasks/<taskId>
Payload:
{
"event": "taskCompleted",
"taskId": "<taskId>",
"jobId": "<jobId>",
"taskType": "PHOTO",
"completed": true,
"completedAt": "...",
"completedBy": "driver01",
"note": "optional"
}
7) Broadcasts and notifications (Server -> App)
- Broadcasts: v1/broadcasts
- User notifications: v1/users/<username>/notifications
Payload example:
{
"type": "broadcast|notification",
"message": "...",
"timestamp": "2025-09-13T22:10:00"
}
## Chat Messaging (App ↔ Server)
Mobile apps exchange chat messages with the backend through dedicated topics. JSON samples can be
found under `src/main/resources/mqtt/chat`.
### App → Server
- **Topic:** `/server/{clientId}/message`
- **Payload example:** `src/main/resources/mqtt/chat/incoming-chat-message.json`
- **Required fields:** `sender`, `receiver`, `content`
- **Optional fields:** `jobId` (Mongo ObjectId), `jobNumber`
- Payloads missing required fields or containing invalid `jobId` values are rejected with a warning log.
### Server → App
- **Topic:** `/client/{receiver}/message`
- **Payload example:** `src/main/resources/mqtt/chat/outgoing-chat-message.json`
- **Notes:** `direction` (INCOMING/OUTGOING) and `messageType` (GENERAL/JOB_RELATED) mirror the
persisted message entity. `read` remains `false` until the receiver acknowledges the message via the
REST API.
### Quality of Service
- Chat topics inherit the global default QoS 2 (`app.mqtt.default-qos`).
- Messages are not retained; offline clients rely on QoS queueing on the broker.
Quality of Service & Retain
- QoS 2 (exactly once) is used by default server side for both inbound subscriptions and outbound publications.
- Retained messages are disabled by default to avoid stale updates.
Error Handling
- Server logs errors; apps should implement local retries for transient failures.
- For request/response patterns over MQTT, include correlationId in payloads if you need strict pairing.
Security
- If authentication is required at broker level, configure username/password.
- Consider using TLS if the broker supports it.
Migration notes
- Previous STOMP destinations like /topic/tasks/{taskId} are now MQTT topics v1/tasks/<taskId>.
- Photos for PHOTO tasks must be embedded in the MQTT message (extraData.photos) published to v1/app/<deviceId>/task/photo/completed. The old HTTP endpoints have been removed.

Binary file not shown.

View File

@@ -0,0 +1,20 @@
package de.assecutor.votianlt.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Separate configuration for PasswordEncoder to avoid circular dependencies
* with VaadinWebSecurity configuration.
*/
@Configuration
public class PasswordEncoderConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -97,6 +97,7 @@ public class AcknowledgmentHandler {
/**
* Handle login request
* Topic: /server/login
*/
private void handleLogin(Map<String, Object> payload) {
try {

View File

@@ -21,8 +21,8 @@ import java.util.concurrent.ConcurrentHashMap;
* Topic Structure (managed internally):
* - Server -> Client: /client/{clientId}/{messageType}
* - Client -> Server: /server/{clientId}/{messageType}
* - ACK Server -> Client: /ack/client/{clientId}/{messageId}
* - ACK Client -> Server: /ack/server/{messageId}
* - ACK Server -> Client: /client/{clientId}/{messageId}/ack
* - ACK Client -> Server: /server/{messageId}/ack
*/
@Slf4j
public class MqttMessagingPlugin implements MessagingPlugin {
@@ -33,12 +33,12 @@ public class MqttMessagingPlugin implements MessagingPlugin {
// Topic templates
private static final String TOPIC_TO_CLIENT = "/client/%s/%s"; // /client/{clientId}/{messageType}
private static final String TOPIC_FROM_CLIENT = "/server/%s/%s"; // /server/{clientId}/{messageType}
private static final String TOPIC_ACK_TO_CLIENT = "/ack/client/%s/%s"; // /ack/client/{clientId}/{messageId}
private static final String TOPIC_ACK_FROM_CLIENT = "/ack/server/%s"; // /ack/server/{messageId}
private static final String TOPIC_ACK_TO_CLIENT = "/client/%s/%s/ack"; // /client/{clientId}/{messageId}/ack
private static final String TOPIC_ACK_FROM_CLIENT = "/server/%s/ack"; // /server/{messageId}/ack
// Subscription patterns
private static final String PATTERN_FROM_CLIENT = "/server/+/%s"; // /server/+/{messageType}
private static final String PATTERN_ACK_FROM_CLIENT = "/ack/server/+"; // /ack/server/+
private static final String PATTERN_ACK_FROM_CLIENT = "/server/+/ack"; // /server/+/ack
private Mqtt5AsyncClient mqttClient;
private ConnectionStateListener connectionListener;
@@ -151,9 +151,21 @@ public class MqttMessagingPlugin implements MessagingPlugin {
try {
notifyConnectionState(ConnectionState.DISCONNECTING, null);
if (mqttClient != null && connected) {
if (mqttClient != null) {
// Check actual client connection state, not just our flag
var clientState = mqttClient.getState();
if (clientState.isConnected()) {
try {
mqttClient.disconnect().join();
log.info("[MqttPlugin] Disconnected successfully");
} catch (Exception disconnectEx) {
// Log but don't throw - client may already be disconnected
log.warn("[MqttPlugin] Disconnect failed (client may already be disconnected): {}",
disconnectEx.getMessage());
}
} else {
log.info("[MqttPlugin] Client already disconnected (state: {})", clientState);
}
}
connected = false;
@@ -163,7 +175,10 @@ public class MqttMessagingPlugin implements MessagingPlugin {
} catch (Exception e) {
log.error("[MqttPlugin] Shutdown failed: {}", e.getMessage(), e);
throw new PluginException("Failed to shutdown MQTT plugin", e);
// Don't throw on shutdown - just log the error
connected = false;
messageHandlers.clear();
ackHandler = null;
}
}
@@ -202,12 +217,30 @@ public class MqttMessagingPlugin implements MessagingPlugin {
throw new PluginException("MQTT client is not connected");
}
messageHandlers.put(messageType, handler);
// Special case for login: subscribe to /server/login (without clientId)
if ("login".equals(messageType)) {
String loginTopic = "/server/login";
log.info("[MqttPlugin] Registering handler for message type '{}' with topic: {}", messageType, loginTopic);
mqttClient.subscribeWith()
.topicFilter(loginTopic)
.qos(MqttQos.EXACTLY_ONCE)
.send()
.whenComplete((subAck, throwable) -> {
if (throwable != null) {
log.error("[MqttPlugin] Subscription to {} failed: {}", loginTopic, throwable.getMessage());
messageHandlers.remove(messageType);
} else {
log.info("[MqttPlugin] Successfully subscribed to: {}", loginTopic);
}
});
} else {
// Standard pattern: /server/+/{messageType}
String topicPattern = String.format(PATTERN_FROM_CLIENT, messageType);
log.info("[MqttPlugin] Registering handler for message type '{}' with pattern: {}", messageType, topicPattern);
messageHandlers.put(messageType, handler);
// Subscribe to the topic pattern
mqttClient.subscribeWith()
.topicFilter(topicPattern)
.qos(MqttQos.EXACTLY_ONCE)
@@ -221,6 +254,7 @@ public class MqttMessagingPlugin implements MessagingPlugin {
}
});
}
}
@Override
public void registerAckHandler(AckHandler handler) throws PluginException {
@@ -296,8 +330,8 @@ public class MqttMessagingPlugin implements MessagingPlugin {
log.debug("[MqttPlugin] Received message on topic: {}", topic);
try {
// Check if it's an ACK message
if (topic.startsWith("/ack/server/")) {
// Check if it's an ACK message (topic ends with /ack)
if (topic.startsWith("/server/") && topic.endsWith("/ack")) {
handleAckMessage(topic, payload);
}
// Check if it's a client message
@@ -317,7 +351,7 @@ public class MqttMessagingPlugin implements MessagingPlugin {
/**
* Handle ACK message from client.
* Topic format: /ack/server/{messageId}
* Topic format: /server/{messageId}/ack
*/
private void handleAckMessage(String topic, byte[] payload) {
if (ackHandler == null) {
@@ -325,10 +359,10 @@ public class MqttMessagingPlugin implements MessagingPlugin {
return;
}
// Extract messageId from topic
// Extract messageId from topic: /server/{messageId}/ack
String[] parts = topic.split("/");
if (parts.length >= 4) {
String messageId = parts[3];
String messageId = parts[2]; // messageId is at index 2
log.debug("[MqttPlugin] Routing ACK for message: {}", messageId);
ackHandler.onAckReceived(messageId, payload);
} else {
@@ -338,11 +372,26 @@ public class MqttMessagingPlugin implements MessagingPlugin {
/**
* Handle client message.
* Topic format: /server/{clientId}/{messageType}
* Topic format: /server/{clientId}/{messageType} or /server/{messageType} (for login)
*/
private void handleClientMessage(String topic, byte[] payload) {
// Extract clientId and messageType from topic
String[] parts = topic.split("/");
// Handle /server/login (without clientId)
if (parts.length == 3 && "login".equals(parts[2])) {
String messageType = parts[2];
ClientMessageHandler handler = messageHandlers.get(messageType);
if (handler != null) {
log.debug("[MqttPlugin] Routing login message (type: {})", messageType);
handler.onMessageReceived(null, payload);
} else {
log.warn("[MqttPlugin] No handler registered for message type: {}", messageType);
}
return;
}
// Handle /server/{clientId}/{messageType}
if (parts.length >= 4) {
String clientId = parts[2];
String messageType = parts[3];

View File

@@ -7,19 +7,12 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@EnableWebSecurity
@Configuration
public class SecurityConfig extends VaadinWebSecurity {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// Konfiguriere zusätzliche öffentliche Endpunkte vor der Basis-Konfiguration