The traccar-realtime-client is a Java Client Software to provide full (remote) control over and interact via live surveillance of your Traccar Server Scenario. It can be used to observe, predict and react to live events - defined by your business. For exmple you can control the timing of a rendez vous point for logistic vehicles.

Real Time Systems

Modern Advanced Driver Assistant Systems (ADAS) can serve as an example of Real Time Systems. They are monitoring the vehicle and observing the environment every instance of the motion. With a 5 GHz network the computational power can take place in large server farms, while the vehicle basically provides events and values from detectors.

The term real time refers to the actual computational results provided as the incident takes place.
Therefor Real Time Systems are systems that can actually interfere the incident in reality. Today ADAS have become Self Driving Systems and can actually drive a car and predict the trajectory by reading the road map.

A real-time system has been described as one which "controls an environment by receiving data, processing them, and returning the results sufficiently quickly to affect the environment at that time".

A system not specified as operating in real time cannot usually guarantee a response within any timeframe, although typical or expected response times may be given. Real-time processing fails if not completed within a specified deadline relative to an event; deadlines must always be met, regardless of system load.

— see https://en.wikipedia.org/wiki/Real-time_computing

GPS Tracking usually implies live tracking and allows us to locate something on a map. Of course live is relativ and we have to rely on connectivity and accept a little latency. For example vehicles can not be GPS tracked in most tunnels due to missing satelite information. A Real Time Client could detect this and make a prediction, when the vehicle will exit the tunnel and be GPS fixed again.

Real Timing

Structuring the data for a real-time application like a Traccar Client is crucial for performance and maintainability.
As a development guideline we do not want to use any back doors to Traccar, like a direct database access. We treat Traccar as a mature component with endless configuration options and we want to communicate the official way, i.e. via Traccar REST API via traccar-api-client.

Traccar is taking care of the connectivity to all (tens of thousands of different) GPS Trackers, stores there info, raises events, while we only need the one connection to the Traccar Server to interact via REST API and retrieve real time data via Websocket. Even, if we would run real-time against the Traccar Database this does not guarantee to be much faster. And there’s a risk to interfere with the Traccar controllers.

We could use an in-memory-database like h2, which is also supported in Spring Boot and Maven. Yet this would have the draw back to create objects during the real time calculation. We will create a Real Time Manager that has all objects in place and state changes occur immediately only restricted by the CPU speed.

Its a race between real time client connectivity and loading against persisting on server side. If you place the traccar-rt-client in the same environment as your Traccar server there are chances that the client is triggered before the database update has finished. Nevertheless the objects are related over Traccar Database IDs, which are indispensable.

when and where

Another important aspect about 'real time development' is the usage of the provided timestamps in chronological order: fixTime, deviceTime and serverTime. The GPS Fixtime describes, when the Position was calculated, fixed and saved into the devices memory. This fixtime is calculated by the GPS Unit hardware and chip in the field with the most precise atomic clocks available. The calculation has to be relativistic and be aware of the satelites positions and much more. The fix is usually made when the parameters provide a result with best accuracy and this can differ due to the environmental conditions.

Important

Real Time Developers should stick to the fixTime in order
to get the actual universal time when and where
the fix (or event) has occured — in reality.

The deviceTime is the point in time when the position and event are sent.
Consecutive messages can have the same fixTime.
The serverTime is the time, when the position and event are persisted.
It can provide clues about the connectivity and network latency.

Real Time Data Structures

Traccar manages its Entities and Relations with an Entity Relational Data Model - ERM. So we need to define a robust data structure to work with Entities and Relations in the RAM, i.e. Software.

Core Design Principles

  • Centralized State
    All data is held in a single, well-defined state object.

  • Normalization
    Each entity type (User, Device, Position, Geofence..) is stored in its own "table" or map. Objects reference each other by ID rather than nesting the full object. This prevents data duplication and makes updates atomic. For example, a Device object will contain a positionId instead of the entire position object.

  • Maps for fast O(1) Lookups
    Data is stored in Maps where the key is the entity’s id. This allows for extremely fast lookups when an update comes in, which is essential for real-time performance.

RealTimeManager

The RealTimeManager is a thread-safe Singleton class to manage the concurrent real-time state of the Traccar client, also known as StateManager in RealTime context.

  • It is annotated with @Component, so Spring creates a singleton for the application context.

  • It uses ConcurrentHashMap for its internal state, which provides thread-safe access and updates. from WebSocket listener background threads.

  private final Map<Long, User>     users     = new ConcurrentHashMap<>();
  private final Map<Long, Device>   devices   = new ConcurrentHashMap<>();
  private final Map<Long, Position> positions = new ConcurrentHashMap<>();
  • Synchronization is used (deviceMapLock) to ensure atomic updates for related maps.

  • The class manages real-time entities (User, Device, Position)
    and their relationships safely across threads.

Note that there is no API involved.

Data Flow & Interaction

Now we need to develop methods for state modifications.
We have to load initial entities and then update them from the live data coming in over a websocket connection.

Basically we will use the RealTimeManager in these sequentiell steps:

  1. Authenticate on Traccar Server and receive a User object with a database ID.
    Store User in RealTimeManager with loginUser(User user).

  2. Load initial data for the scenario by fetching the entities from Traccar and converting them to a Map.
    Optionally collect last known Position objects for the Devices.

  3. Connect to websocket and receive live data.
    Traccar pushes JSON object arrays to be transformed and then updated on individual entities.

Have a look at these RealTimeManagerIT snippets to identify the steps listed above as you read on:

class RealTimeManagerIT extends BaseReaTimeScenarioTest { // sets up scenario

    @Autowired protected Api api;
    private RealTimeManager stateManager = RealTimeManager.getInstance();

    // login
    api.setBasicAuth(scenario.admin.getEmail(), scenario.admin.getPassword());
    stateManager.loginUser(scenario.admin);

    // initialize
    initialUsers = api.getUsersApi().getUsers(null);
    stateManager.loadInitialUsers(initialUsers);
    stateManager.getAllUsers() ...

    // same for devices and other entities

The last simulation of a server update is purely logical and zero websocket technology.
The line

    stateManager.addOrUpdatePosition(newPosition);

'simulates' a Position that could come in via WebSocket or any other ETL to a controller that invokes methods to change the real time state.

Now that we can safely read from the RealTimeManager
we will create a RealTimeController for synchronization.

RealTimeController

The RealTimeManagerIT test is holding the private RealTimeManager in order to test it’s methods.

The RealTimeController is the subsequent implementation keeping the RealTimeManager private to control it’s state by synchronizing the server updates. The Controller encapsulates all the logic for communicating with the Traccar server. It handles authentication, fetches the initial state via the REST API, and then connects to the WebSocket to receive and process live updates, feeding everything into the thread-safe state manager.

Basic RealTimeController design:

@Component                                                    (1)
public class RealTimeController {

  private final RealTimeManager stateManager;                 (2)

  public RealTimeController(RealTimeManager stateManager) {   (3)
    this.stateManager = stateManager;
  }

  @Autowired protected Api api;                               (4)
  @Autowired private WebSocketRoute liveConnection;           (5)
  1. @Component: Spring creates a singleton

  2. private final RealTimeManager exclusive to controller

  3. Instantiated by the Spring framework. Injects the RealTimeManager dependency via constructor injection.

  4. Api to access the server actively

  5. Traccar WebSocketRoute for updates in the background

We will look at the TraccarWebSocketRoute soon.
For now you can run the RealTimeControllerIT which basically executes the line

  controller.loginAndInitialize(scenario.admin)

where the authentication, loading of initial server Entities and establishing a live connection via WebSocket.

RealTimeControllerIT

This test initializes Camel and rt controller

bm.traccar.rt.RealTimeControllerIT    : Starting RealTimeControllerIT ..
camel start ..
bm.traccar.rt.RealTimeControllerIT    : Started RealTimeControllerIT in 3.186 seconds

loads the scenario on the server (base test)

bm.traccar.rt.scenario.ScenarioLoader : --- Setup scenario on server ---

then the controller is authenticated via server and can load the scenario in the rt manager:

bm.traccar.rt.RealTimeController      : --- Loading initial users from server...   ---
bm.traccar.rt.RealTimeController      : --- Loading initial devices from server... ---

with server entities.
The we can see TraccarWebSocketRoute handshake to obtain a sessionId, the creation of a VertxWebsocketEndpoint and finally the user is logged in the rt controller:

bm.traccar.ws.TraccarWebSocketRoute   : Obtained JSESSIONID: node015pugc...4ehrxl88u6sl19.node0
o.a.c.c.v.w.VertxWebsocketEndpoint    : Connected to WebSocket on localhost:80
bm.traccar.rt.RealTimeController      : User logged in: admin

And now the software is running an waiting for server updates:

traccarWebSocketDynamicRoute          : Received Traccar WebSocket update: {"positions":[]}
traccarWebSocketMessageProcessingRoute:    Process WebSocket raw  message: {"positions":[]}
traccarWebSocketMessageProcessingRoute:    Process WebSocket json message: {positions=[]}

Real Time Updates

As indicated the TraccarWebSocketRoute updates the TraccarStateManager similar the the controller.

We already had chosen Apache Camel for integration purposes and the TraccarWebSocketRoute is a complete Camel implementation and is described in more detail here.

sample messages

To keep a distributed systems integrity you need to have all models in mind.

The Model for Tracker Development defines a GPS message from a unique device.
The REST yml file provides a bigger model for a complete GTS.
These models deviate and must be mapped to each other.

The following devices and positions messages in json format look like this.

{devices=
  [
    {id=554, attributes={}, groupId=0, calendarId=0, name=runner, uniqueId=10, status=online,
     lastUpdate=2026-02-19T19:46:41.304+00:00, positionId=198, phone=null, model=ro,
     contact=null, category=null, disabled=false, expirationTime=null
    }
  ]
}

Note the difference of the devices database id=554 and the uniqueId=10. The database Id is technical and can change in a different environment, while the uniqueId always refers to a unique tracker. The latter must be used in a scenario to track the device.

Anyhow the database Ids are important for referencing.
The above device references the positionId=198,
vice versa the position has the reference deviceId=554.

{positions=
  [
    {id=198,
    attributes={distance=0.0, totalDistance=0.0, motion=true}, deviceId=554, protocol=osmand,
    serverTime=2026-02-19T19:46:41.300+00:00,
    deviceTime=2026-02-19T19:46:41.000+00:00,
       fixTime=2026-02-19T19:46:41.000+00:00,
    valid=true, latitude=52.0, longitude=13.0, altitude=30.0, speed=10.0, course=20.0,
    address=null, accuracy=0.0, network=null, geofenceIds=null
    }
  ]
}

conclusion

This Java structure provides a clean, scalable, and safe way to manage your Traccar client’s data.
Your REST API client would call methods like loginUser and loadInitialDevices,
while your WebSocket client’s onMessage handler would call addOrUpdatePosition and/or other Entities.
The rest of your application can then safely read from the TraccarStateManager to display data.

CLI Quick Start

After creating the three layers around the ApiClient, Api interface and RealTimeManager these are combined in the @SpringBootApplication RealTimeClient.

run RealTimeClient CLI

The traccar-rt-client does not provide any webservices. It is a plain command line interface (CLI) to connect to a traccar server from the command line and read what’s going on.

@SpringBootApplication
public class RealTimeClient implements CommandLineRunner

Therefor it is a solid building block for real time applications. You can integrate it in your application and code against the RealTimeManager as if it was the traccar server.

pick up the traccar-rt-client-6.10.0-RC.jar

put it in /someFolder/traccar-rt-client-6.10.0-RC.jar

(release url)

copy app.props

enter credentials

run java, mvn spring:run ..

start your traccar client or your tracker connected to traccar

watch the console over the map

If you don’t have a traccar server you can sign up at one of traccar’s demo servers.

RealTimeClient is a Spring Boot application that:

  • Starts RealTimeController on launch.

  • Reads admin credentials from application.properties.

  • Logs in as the admin user when starting.

  • Remains running until manually shutdown (e.g., Ctrl+C), and supports graceful shutdown.

    modify RealTimeClient to a @SpringBootApplication that starts the RealTimeController
    and log in as traccar.admin.name as provided in the application.properties and can be gracefully shutdown manually
    You can run the application as usual (e.g., with mvn spring-boot:run or java -jar),
    and it will log in and initialize the real-time controller using the configured admin credentials.

The Traccar WebSocket Client

The traccar-api-client is a Java Client Software to request real time data from your Traccar Server. While the REST API is based on single requests, i.e. REST calls and responses without any session handling, i.e. stateless.

Therefor this WebSocket Route/Session Manager can create a Camel channel for each individual registered user to manage WebSocket connections and event routing.

On a higher level different websocket channels can report live events in a given Scenario. For example you could direct different vehicles to a rendezvous point and your software can create events as they approach each other.

The Traccar WebSocket API

Our starting point is the specification at Traccar WebSocket API, which is actually so brief that we can reprint it here:

In addition to the REST API, we provide an access to a WebSocket endpoint for live updates.
Endpoint for the WebSocket connection: /api/socket
Session cookie is the only authorization option for the WebSocket connection.
Each message in the WebSocket stream uses the same universal JSON format:

{
  "devices": [...],
  "positions: [...],
  "events": [...]
}
— see www.traccar.org/traccar-api

Websocket Handshake

Let’s break down the websocket spec above towards an implementation.

Endpoint

The primary WebSocket endpoint for Traccar is typically /api/socket.
The WebSocket URL would be ws://your.traccar.server:8082/api/socket
(or wss:// for secure connections).

Authentication

Traccar’s WebSocket API relies solely on a session cookie JSESSIONID for authentication.
You cannot use Basic Auth or Bearer tokens directly with the WebSocket connection itself.

In order to create a ws session you need to authenticate (as in traccar-api-client or traccar-api-camel) with a successful HTTP POST request to the Traccar /api/session endpoint to log in.
Then extract the JSESSIONID cookie from the response headers of this login request and use this JSESSIONID cookie when establishing the WebSocket connection.

WebSocket Component

As this repository is based on Spring Boot with Camel integration,
i.e. we are building a Camel Spring Boot WebSocket Client for Traccar.

Besides Spring Boot and Camel we need to choose a websocket component.
Most popular choices are the Eclipse Vert.x and the Eclipse Jetty WebSocket Client.

Jetty is actually a traditional servlet-container with a thread based architecture in Java. As Traccar works with a Jetty Server it could be chosen for symetrie reasons. Anyhow we only want to rely on the specifications and not on proprietary implementations.

Vert.x is a modern event driven non blocking implementation comparable to netty, which we have analyzed in depth in the jeets project. Vert.x has a better scalability for a larger number of connections and a lower latency. It also offers http, database drivers and smooth integration with other technologies.

… and the winner is … Vert.x
Of course we will use the camelized version with a Spring Boot Starter in Maven to abstract the WS component (in case we revise the decision later):

       <groupId>org.apache.camel.springboot</groupId>
    <artifactId>camel-vertx-websocket-starter</artifactId>

With Camel we can configure the appropriate security settings,
leverage Camel’s asynchronous nature to handle multiple WebSocket connections and messages efficiently
and implement a backpressure mechanisms in case of high message volumes.

A basic route implementation looks like this:

    public void configure() throws Exception {
        from("vertx-websocket:ws://your-traccar-server:port/api/socket") (1)
            .log("Received WebSocket message: ${body}")
            .to("bean:yourMessageHandler");
    }
  1. use wss:// for secure connections

But first we need to extract the JSESSIONID from a http POST and create a Cookie,
while using the http context from Spring Boot for the ws connection.

Note that we will not use the traccar-api-client for the http POST, since we clearly want to separate the two clients for API and WebSocket. Our architecture does not want any dependencies between them for isolated testing etc. Each client represents a software Unit and can be tested, developed and used alone. And like every Unit it should be replaceble.

Anyhow we can combine the two in a higher level traccar-java-client.

Implementation with Camel Routes

The core of this traccar-ws-client is the TraccarWsClientRoute.
Here is a rough outline with three routes:

@Component
public class TraccarWsClientRoute extends RouteBuilder {

  @Override
  public void configure() throws Exception {

    from("timer:traccarLoginTimer?period=3600000") // every hour
        .routeId("traccarLoginRoute")                              (1)
          ...
        .setBody(simple("email=" + email + "&password=" + password)) // credentials
        .toD(host + "/api/session") // login
        .process(
            exchange -> {
              // get 'Set-Cookie' header from response

              // Extract JSESSIONID from the Set-Cookie header

              // set exchange property as parameter for the next ws route

              // Trigger traccarWebSocketConnectionRoute           (2)
            })
          ...
        .end();

    from("direct:connectTraccarWebSocket")
        .routeId("traccarWebSocketConnectionRoute") (2)
        .process(
            exchange -> {
              // get JSESSIONID from Route                         (1)

              // set Cookie Header with JSESSIONID for initial handshake

        .toD("vertx-websocket:" + traccarWebSocketUrl + "&handshake.Cookie=${header.Cookie}")
        .log("Connected to Traccar WebSocket.")
        .to("direct:traccarWebSocketMessages"); (3)

    from("direct:traccarWebSocketMessages")
        .routeId("traccarWebSocketMessageProcessingRoute")         (3)
        .unmarshal()
        .json(JsonLibrary.Jackson)
        .log("Received Traccar WebSocket update: ${body}");
  1. Route to trigger http POST

  2. Route to connect websocket with handshake

  3. Route to receive live data

Notes

  • The dynamic vertx-websocket URL is proprietary and involves the handshake parameter.
    The Jetty component (websocket) handles cookies different and requires an explicit header setting.

  • We are not processing Cookies with
    java.net.CookieHandler, CookieManager, CookieStore, CookiePolicy, HttpCookie etc.
    In the first http route we are simply applying String functions to extract the JSESSIONID.

  • You are responsible or security using Spring Cloud Config, Kubernetes Secrets, environment variables, a secrets management solution like Vault or whatever you chose for your software.

Debugging

The initial traccar-ws-client implementation is kept as simple as it gets. This allows a better understanding and makes it easier to expand the component inside the application lifecycle development.

Nevertheless networking is alway a challenge and in this case you might experience problems with (reverse) proxies, firewalls, closed ports, URL / URI resolution, cloud installation, docker, port forwarding, Traccar server settings and beware of misleading AI assistance.

Therefor you can execute the most simple test with two cammand lines, before you drill deeper.

If you run into any problems apply the command line tools curl for the http POST and wscat for the websocket connection. In addition you should create an account on one of the graciously sponsered Traccar Demo Servers and test with https and email/password.

Step1: http with curl

Compose the command line with email/password/http|s/port and check the http connection with

curl -v POST -d "email=admin@domain.com&password=admin" http://localhost:8082/api/session

and hopefully get something like this

> POST /api/session HTTP/1.1                             (1)
> Host: localhost:8082
>
< Set-Cookie: JSESSIONID=node0q7h2lu4fsi40101wtkbz590i060.node0; Path=/; HttpOnly  (2)
< Server: Jetty(11.0.25)                                 (3)
<
* Connection #1 to host localhost left intact            (4)
  {...  "name":"admin",                                  (5)
       "email":"admin@domain.com"  ...}
  1. > for outgoing POST

  2. < for incoming response with a JSESSIONID

  3. Traccar’s Jetty Server version.

  4. make sure connection is kept up

  5. JSON DTO for logged user

Step2: ws with wscat

Again, adopt your parameters and copy the JSESSIONID from the http POST above:

wscat -c ws://localhost:8082/api/socket \
      -H "Cookie: JSESSIONID=node0q7h2lu4fsi40101wtkbz590i060.node0"
Connected (press CTRL+C to quit)
< {"positions":[]}
< {}

Here you should get the Connected response and see the messages arriving in intervals (configured on the server). If you want to see actual values you might change something in the Traccar Frontend or register a device or the like.

Introduce a typo or use a wrong JSESSIONID to raise

error: Unexpected server response: 503

which you might find in your java stack as

WebSocket upgrade failure: 503

To debug UpgradeRejectedException: WebSocket upgrade failure: 503 on server side you should start here:

AsyncSocketServlet extends JettyWebSocketServlet
configure()
    userId = loginService.login(token).getUser().getId();

This way you have validated all parameters and are good to go for comparing analyses as you develop.

review traccar-api-client

simple example / usage

Here’s a simple example code to demo how to add the Traccar API Service to your company software.
First you need these prerequesites:

  • Add the traccar-api-client-x.y.z.jar to your application.
    Or add it to your .m2 repo or nexus server, then to your pom.

  • Add your Traccar Server URL and credentials to your application.properties file.
    i.e. host, user.name, user.password, user.email (, accountToken).
    Or provide these values in a way that meets your security strategy (secrets etc.)

and then the coding is straight forward:

package your.company.app...;

import bm.traccar.api.ApiService            // (1)

public class TraccarUsers {

    @Autowired
    private ApiService api;                 // (1)

    public static void main(String[] args) {

        api.setBasicAuth(mail, password);   // (2)
//      api.setBearerToken(token);

        User user = new User();             // (3)
        user.setName("user-1");
        user.setEmail("email-1");
        user.setPassword("pw-1");

        User userOnServer = api.users.createUser(user);     // (4)

    }
}
  1. add the ApiService, being a Spring @Service class

  2. choose and set your authentication
    and can change it any time in the program flow.

  3. create a new User (DTO) in your software for your employees

  4. create a new User (Entity) in your Traccar Server
    The new User (DTO) is returned with a unique ID from the server.

And that is all you need to create users, devices etc. and manage them in your code!
For more examples check the integration tests, i.e. UsersIT and others.

configuration

For convenience you can use the application.properties file and grab the @Value in your source code, depending on your security concepts. Check the *.*IT integration test files for demo code.

application.properties

# traccar server
traccar.host=http://localhost
traccar.user.name=admin
traccar.user.password=admin
traccar.user.email=admin@domain.com

your implementation (compare ITests)

@Value("${traccar.user.name}")     private String name;
@Value("${traccar.user.password}") private String password;
@Value("${traccar.user.email}")    private String mail;

usage

After adding the jar to your software or build system the API can easily be added to any Springboot Application as a @Service in your code

@Autowired private ApiService api;