DEV Community

jayanti-neu
jayanti-neu

Posted on • Edited on

๐Ÿšฆ Real-Time Freight Tracker with Java & WebSockets (Phase 2)

After building the core CRUD backend in Phase 1, Phase 2 of the Freight Tracker project adds real-time updates using WebSockets. This allows clients to receive live status changes for shipments without needing to refresh the page.

Its continuation of building a full-stack logistics tracking app, this post adds real-time updates using Spring Boot, STOMP, and WebSockets.


๐ŸŒ Why Real-Time with WebSockets?

In logistics applications, users (like clients, warehouse staff, or support teams) often need instant updates on shipment status. Polling the backend repeatedly isnโ€™t efficient โ€” WebSockets provide a two-way communication channel where the server pushes updates to connected clients instantly.

Spring Boot supports this via:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

โš™๏ธ Setting up WebSockets in Spring Boot

WebSocketConfig.java

We created a new class called WebSocketConfig.java to enable STOMP messaging and define endpoints:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*")
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
        registry.setApplicationDestinationPrefixes("/app");
    }
}
Enter fullscreen mode Exit fullscreen mode

Two Sides of WebSocket Communication

When using STOMP over WebSockets in Spring, there are two types of message flow:

Client โ†’ Server (incoming messages)

Server โ†’ Client (broadcast messages)

We configure these in two parts:

registerStompEndpoints(): Where clients initially connect (like /ws)

configureMessageBroker(): How messages are routed internally (topics, queues, etc.)

๐Ÿ”‘ Keywords Explanation:

  • @Configuration: Marks the class as a Spring config class which meansย it processes the class during startup
  • @EnableWebSocketMessageBroker: Turns on WebSocket messaging with STOMP protocol and enables the broker system (/topic or /queue). It allows you to also map /app/... for client โ†’ server messages
  • .addEndpoint("/ws"): Clients connect to this endpoint
  • withSockJS(): Adds fallback support if browser doesn't support native WebSockets
  • enableSimpleBroker("/topic"): Clients subscribe to these topics to receive updates
  • setApplicationDestinationPrefixes("/app"): Prefix for client-to-server messages

๐Ÿ“ข Broadcasting Status Updates

To send messages to subscribed clients, we created a broadcaster class:

ShipmentStatusBroadcaster.java

@Component
public class ShipmentStatusBroadcaster {
    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    public void broadcastUpdate(ShipmentUpdateMessage message) {
        messagingTemplate.convertAndSend("/topic/shipments", message);
    }
}
Enter fullscreen mode Exit fullscreen mode

SimpMessagingTemplate is used to send messages to WebSocket subscribers (like RestTemplate is used for HTTP).

Why Do We Need SimpMessagingTemplate?
The service has business events (e.g., shipment status updated) and we want to push those events to clients manually โ€” not in response to a client message, but whenever the backend decides (like after saving to DB).

SimpMessagingTemplate is a helper that talks directly to the broker we created.

SimpMessagingTemplate.convertAndSend() = Pushes events into the broker

๐Ÿง  What is @Component?

This marks the class as a Spring-managed bean. It gets picked up automatically during component scanning and injected wherever needed (like in the ShipmentController).


๐Ÿ“ฆ Updating the Controller

We modified the ShipmentController to send broadcast messages on every shipment creation or update:

@PostMapping
public Shipment createShipment(@RequestBody Shipment shipment) {
    shipment.setLastUpdatedTime(LocalDateTime.now());
    Shipment saved = shipmentRepository.save(shipment);

    broadcaster.broadcastUpdate(
        ShipmentUpdateMessage.builder()
            .shipmentId(saved.getId())
            .trackingNumber(saved.getTrackingNumber())
            .status(saved.getStatus())
            .lastUpdatedTime(saved.getLastUpdatedTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
            .build()
    );

    return saved;
}
Enter fullscreen mode Exit fullscreen mode

We do the same in the PUT /api/shipments/{id} method.

Frontend (subscribe) ----connect---> /ws endpoint
|
| Subscribes to /topic/shipments
v
Spring Broker (enabled by enableSimpleBroker)
|
| convertAndSend("/topic/shipments", dto)
v
Frontend receives update in real-time


๐Ÿ“จ DTO for Real-Time Updates

ShipmentUpdateMessage.java

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ShipmentUpdateMessage {
    private Long shipmentId;
    private String trackingNumber;
    private ShipmentStatus status;
    private String lastUpdatedTime;
}
Enter fullscreen mode Exit fullscreen mode

We keep this DTO separate from the main Shipment model to ensure:

  • Lightweight messages
  • No overexposing of sensitive data
  • Easy control over WebSocket response structure

๐Ÿ”Œ Frontend Client Example

Hereโ€™s how a basic HTML frontend listens for shipment updates:

<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>

<script>
const socket = new SockJS("http://localhost:8080/ws");
const stompClient = Stomp.over(socket);

stompClient.connect({}, function (frame) {
  console.log("Connected: " + frame);
  stompClient.subscribe("/topic/shipments", function (message) {
    const body = JSON.parse(message.body);
    console.log(`Shipment #${body.trackingNumber} is now ${body.status} at ${body.lastUpdatedTime}`);
  });
});
</script>
Enter fullscreen mode Exit fullscreen mode

Every time a shipment is created or updated, this client will receive a message in real time.


๐Ÿง  What I Learned in Phase 2

  • How STOMP protocol simplifies WebSocket communication
  • How to use @EnableWebSocketMessageBroker to configure endpoints
  • How SimpMessagingTemplate is used to push messages
  • When and why to use DTOs for WebSockets
  • How real-time updates improve client experience in logistics

๐Ÿ—“๏ธ Coming Next

  • React-based frontend dashboard
  • Service Layer refactor and error handling
  • Deployment to cloud with Docker and AWS

Thanks for reading โ€” you can find the code here: https://github.com/jayanti-neu/freight-tracker ๐Ÿš€

Top comments (0)