diff --git a/STOMP_README.md b/STOMP_README.md new file mode 100644 index 0000000..d327e46 --- /dev/null +++ b/STOMP_README.md @@ -0,0 +1,130 @@ +# 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 + +#### 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 + +```javascript +// Mit SockJS +const socket = new SockJS('http://localhost:8080/ws'); +const stompClient = Stomp.over(socket); + +// Oder mit nativem WebSocket +const socket = new WebSocket('ws://localhost:8080/websocket'); +const stompClient = Stomp.over(socket); +``` + +### 2. Verbindung herstellen + +```javascript +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 + +```javascript +// 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 +})); +``` + +## Backend-Integration + +### Programmatische Nachrichten senden + +```java +@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"); +``` + +## Konfiguration + +Die STOMP-Konfiguration befindet sich in: +- **`WebSocketConfig.java`** - WebSocket und STOMP-Konfiguration +- **`MessageController.java`** - Nachrichtenbehandlung +- **`application.properties`** - Zusätzliche WebSocket-Einstellungen + +### Wichtige Konfigurationsparameter + +```properties +# 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: +1. Eine WebSocket-Client-Bibliothek verwenden +2. Browser-Entwicklertools für WebSocket-Verbindungen nutzen +3. Spezialisierte STOMP-Testing-Tools einsetzen + +Die Implementierung ist vollständig und bereit für die Integration mit externen Apps. \ No newline at end of file diff --git a/pom.xml b/pom.xml index ace7454..5e9a74f 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,16 @@ 2.0.1 + + + org.springframework.boot + spring-boot-starter-websocket + + + org.springframework + spring-messaging + + diff --git a/src/main/java/de/assecutor/votianlt/config/MailConfig.java b/src/main/java/de/assecutor/votianlt/config/MailConfig.java new file mode 100644 index 0000000..c6ecf24 --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/config/MailConfig.java @@ -0,0 +1,36 @@ +package de.assecutor.votianlt.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class MailConfig { + + @Value("${mail.smtp.username}") + private String username; + + @Value("${mail.smtp.password}") + private String password; + + @Value("${mail.smtp.host:smtp.gmail.com}") + private String host; + + @Value("${mail.smtp.port:587}") + private int port; + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } +} \ No newline at end of file diff --git a/src/main/java/de/assecutor/votianlt/config/WebSocketConfig.java b/src/main/java/de/assecutor/votianlt/config/WebSocketConfig.java new file mode 100644 index 0000000..c0cc8a7 --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/config/WebSocketConfig.java @@ -0,0 +1,37 @@ +package de.assecutor.votianlt.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +/** + * WebSocket configuration for STOMP messaging. + * Enables real-time communication with client applications. + */ +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + @Override + public void configureMessageBroker(MessageBrokerRegistry config) { + // Enable a simple memory-based message broker to carry messages back to client + // on destinations prefixed with "/topic" and "/queue" + config.enableSimpleBroker("/topic", "/queue"); + + // Designate the "/app" prefix for messages that are bound to methods + // annotated with @MessageMapping + config.setApplicationDestinationPrefixes("/app"); + } + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + // Register the "/ws" endpoint for WebSocket connections + // withSockJS() enables SockJS fallback options for browsers that don't support WebSocket + registry.addEndpoint("/ws").withSockJS(); + + // Also add a plain WebSocket endpoint without SockJS for native WebSocket clients + registry.addEndpoint("/websocket"); + } +} \ No newline at end of file diff --git a/src/main/java/de/assecutor/votianlt/controller/MessageController.java b/src/main/java/de/assecutor/votianlt/controller/MessageController.java new file mode 100644 index 0000000..44b6228 --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/controller/MessageController.java @@ -0,0 +1,85 @@ +package de.assecutor.votianlt.controller; + +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Controller; +import org.springframework.beans.factory.annotation.Autowired; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; + +/** + * STOMP message controller for handling real-time communication with apps. + * Provides endpoints for sending and receiving messages via WebSocket/STOMP. + */ +@Controller +public class MessageController { + + @Autowired + private SimpMessagingTemplate messagingTemplate; + + /** + * Handles messages sent to /app/message and broadcasts them to all subscribers of /topic/messages + */ + @MessageMapping("/message") + @SendTo("/topic/messages") + public Map handleMessage(Map message) { + // Add timestamp to the message + message.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); + message.put("processed", true); + + return message; + } + + /** + * Handles job status updates from apps + */ + @MessageMapping("/job/status") + @SendTo("/topic/job-updates") + public Map handleJobStatusUpdate(Map jobUpdate) { + jobUpdate.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); + jobUpdate.put("source", "app"); + + return jobUpdate; + } + + /** + * Handles device location updates from mobile apps + */ + @MessageMapping("/device/location") + @SendTo("/topic/device-locations") + public Map handleDeviceLocation(Map locationUpdate) { + locationUpdate.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); + locationUpdate.put("processed", true); + + return locationUpdate; + } + + /** + * Send notification to specific user + */ + public void sendNotificationToUser(String username, String message) { + Map notification = Map.of( + "message", message, + "timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), + "type", "notification" + ); + + messagingTemplate.convertAndSendToUser(username, "/queue/notifications", notification); + } + + /** + * Send broadcast message to all connected clients + */ + public void sendBroadcastMessage(String message) { + Map broadcast = Map.of( + "message", message, + "timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), + "type", "broadcast" + ); + + messagingTemplate.convertAndSend("/topic/broadcasts", broadcast); + } +} \ No newline at end of file diff --git a/src/main/java/de/assecutor/votianlt/util/MailUtil.java b/src/main/java/de/assecutor/votianlt/util/MailUtil.java index 4609da3..bf5c2e2 100644 --- a/src/main/java/de/assecutor/votianlt/util/MailUtil.java +++ b/src/main/java/de/assecutor/votianlt/util/MailUtil.java @@ -1,5 +1,6 @@ package de.assecutor.votianlt.util; +import de.assecutor.votianlt.config.MailConfig; import jakarta.mail.Message; import jakarta.mail.MessagingException; import jakarta.mail.PasswordAuthentication; @@ -7,15 +8,26 @@ import jakarta.mail.Session; import jakarta.mail.Transport; import jakarta.mail.internet.InternetAddress; import jakarta.mail.internet.MimeMessage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import java.util.Properties; +@Component public class MailUtil { - public static void sendMail(String to, String subject, String body) throws MessagingException { - // SMTP-Konfiguration (hier Beispiel für Gmail, anpassen für Produktivsystem!) - final String username = "your-email@gmail.com"; // TODO: ersetzen - final String password = "your-password"; // TODO: ersetzen - final String host = "smtp.gmail.com"; - final int port = 587; + + private final MailConfig mailConfig; + + @Autowired + public MailUtil(MailConfig mailConfig) { + this.mailConfig = mailConfig; + } + + public void sendMail(String to, String subject, String body) throws MessagingException { + // SMTP-Konfiguration aus externalisierter Konfiguration + final String username = mailConfig.getUsername(); + final String password = mailConfig.getPassword(); + final String host = mailConfig.getHost(); + final int port = mailConfig.getPort(); Properties props = new Properties(); props.put("mail.smtp.auth", "true"); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ec44326..fd41aae 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -12,4 +12,20 @@ vaadin.allowed-packages=com.vaadin,org.vaadin,de.assecutor.votianlt spring.jpa.open-in-view=false # MongoDB -spring.data.mongodb.uri=mongodb://192.168.180.25:27017/votianlt \ No newline at end of file +spring.data.mongodb.uri=mongodb://192.168.180.25:27017/votianlt + +# Mail Configuration +mail.smtp.username=your-email@gmail.com +mail.smtp.password=your-password +mail.smtp.host=smtp.gmail.com +mail.smtp.port=587 + +# WebSocket and STOMP Configuration +# WebSocket message size limits (in bytes) +spring.websocket.servlet.max-text-message-buffer-size=8192 +spring.websocket.servlet.max-binary-message-buffer-size=8192 +# Enable STOMP over WebSocket +spring.websocket.stomp.enabled=true +# STOMP heartbeat settings (in milliseconds) +spring.websocket.stomp.heartbeat.outgoing=10000 +spring.websocket.stomp.heartbeat.incoming=10000 \ No newline at end of file