The traccar-api-client is a Java Client Software
to provide full (remote) control over your Traccar Server.
It can be used to integrate your Tracking with your company systems, like Human Resources-, Sales- and Fleet Management Software. Synchronize your employees with their cars and driver information without utilizing the Traccar UI. Pull driving reports for your sales team … and much more ;)
Architectural Characteristics
-
Separation of Concerns
The architecture separates generated API code intraccar-openapitools-client
from business logic viaApi interfaceintraccar-api-client. -
Generated API Layer
All REST API models and client classes are generated from the Traccar OpenAPI spec (yaml)
and maintained intraccar-openapitools-client. -
Extensible Client
traccar-api-clientwraps and extends the generated API,
providing a stable interface for application developers.
Component Interaction
This sequence diagram illustrates how components interact at runtime for REST API calls:
Traccar API @Service
The Traccar OpenAPI Client wraps the Traccar API in a Spring @Service — simple as that.
You can always counter check against the latest
Official Traccar API Reference.
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;
authentication
Then you can set your credentials via bearer token or basic authentication
// use your token generated from your Traccar server api.setBearerToken(YOUR_TRACCAR_TOKEN); // switch auth method api.setBasicAuth(mail, password); // switch for every REST call as needed
or switch users with different access to different user, devices etc.
directly in your code.
see package bm.traccar.invoke.auth
usage example
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.jarto your application.
Or add it to your.m2repo or nexus server, then to yourpom. -
Add your Traccar Server URL and credentials to your
application.propertiesfile.
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)
}
}
-
add the
ApiService, being a Spring@Service class -
choose and set your authentication
and can change it any time in the program flow. -
create a
new User(DTO) in your software for your employees -
create a
new User(Entity) in your Traccar Server
The new User (DTO) is returned with a unique ID from the server.
mimic URL syntax
Note how the Java invokation mimics a REST call
// <server>/api/users?user_Id=11 User user = api.users.getUsers(userId);
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.
As you can see the above expression is similar the URL
http://{host}:{port}/api/users/{id}
User userOUT = api.users.createUser(user);
https://localhost/api/devices?id=3 Device[] devices = api.devices.getById(3);
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.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;
fixed docker authentication
For integration testing with docker we apply a fixed serviceAccountToken
to gain (virtual) administration over the complete server.
traccar.web.serviceAccountToken=VIRTUAL_ADMIN_ACCESS
@Value("${traccar.web.serviceAccountToken}") private String virtualAdmin;
|
Important
|
You have to prepare your docker version of traccar via <entry key='web.serviceAccountToken'>VIRTUAL_ADMIN_ACCESS</entry> |
Traccar API Interface
If you don’t want to use the Traccar @Service and rather implement your own,
you should start with the Traccar `API interface.
The coding is similar (and using the same implementations)
package your.company.app...;
import bm.traccar.api.Api // (1)
public class TraccarUsers {
@Autowired
private Api 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");
// ApiService syntax
User userOnServer = api.users.createUser(user);
// Api interface syntax // (4)
User userOnServer = api.getUsersApi().createUser(user);
}
}
-
this time we are auto wiring the
Api interface -
choose and set your authentication
-
create a
new User(DTO) in your software for your employees -
create a
new User(Entity) on your Traccar Server
By using the Api interface you can also get individual
sub API implementations:
Api.Users users = api.getUsersApi() User newUser = users.createUser(user);
This can be convenient, if you don’t want to mess with many APIs and focus the implementation on User Management, for example.
Traccar API Implementation
This software is based on a generated Java Spring REST Client from the Traccar REST specification.
The single package (bm.traccar.api) implementation wraps the Java client in a Spring @Service
to be applied wherever feasible. The API client completely hides the REST intricities
inside regular Java methods.
The traccar-api-client is basically a single package application or rather service.
In addition there is a websocket package to receive live information from the Tracking System.
openapi-generator
As Traccar provides a REST specification in form of a yaml file we faciliate the
OpenAPI Generator
OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec v3.
Note that this Java code generation takes place in the isolated
traccar-openapitools-client
project. It has to run before this project which is build around the generated artifact.
|
Tip
|
To get more details on the generation you can read the project notes: openapitools-client |
You can check Generate the client using Maven for a starting point on how to apply Maven properties etc.
Traccar REST
Traccar v6.7 provides 17 APIs (see package bm.traccar.generated.api)
and as the GTS progresses Traccar v6.12.2 provides 19 APIs.
These APIs support many aspects of GPS Tracking and beyond, like health monitoring.
The traccar-api-client defines one single (nested) API interface:
bm.traccar.api.Api, which allows us to switch the implementation.
This was required, for example, when changing from swagger to OpenAPI (3 to 3.1).
|
Tip
|
Therefor you should stick to the |
Data Transfer Objects - DTOs …
In addition the traccar-api-client-6.7 provides 21 Data Transfer Objects
and v6.12.2 already has 26 (package bm.traccar.generated.model.dto).
These DTO represent the different System Entities
and are used to exchange information with the server.
For example the User API call to
Create a User
is provided as Java method by the Api interface:
public interface Api {
interface Users {
User createUser(User user) throws ApiException;
In your code you can easily create a new User() in your software
User userIN = new User();
userIN.setName(usr);
userIN.setEmail(mail);
userIN.setPassword(pwd);
userIN. ...
and then create it on server side and receive a copy:
User userOUT = api.users.createUser(userIN);
Note that userIN and userOUT are different instances.
The latter provides the userId provided by the Traccar Model
and is vital to use in the client server communication.
The ID is usually hidden for external users (i.e. in the UI),
but required for unambiguously user identification!
… are not System Entities
Nevertheless you should always distinguish Data Transfer Objects from actual Entities in the Systems Entity Relations Model - ERM. The Traccar System is build around the Traccar Data Model, which is represented in the model package, while the DTOs are created in the resource package. The Entities are only accessed by Traccar itself, while DTOs are snapshots of them. The Traccar Database is the best place to monitor what’s going on.
This is similar to DNA and RNA:
The DNA (ERM) is part of the living organic System,
while the RNA (DTOs) is simply a copy of the DNA from a certain point in time.
Traccar sub Api implementations
This project is part of a larger software and therefor the Traccar Api Endpoints (operationId)
are implemented in the order required by the main application.
During prototyping you should avoid any bulk implementations.
Protoyping is modelling and every new cycle, i.e. x.y.z release
can introduce new sub APIs and endpoints - as the main application progresses.
This way the implementation for all sub APIs turns into a mature pattern.
And interdependencies can be implemented with convenient methods combining common actions.
The most important aspect of this project is its layer functionality. As the Traccar GTS progresses the REST specification is modified and we want to avoid code changes all over our application. In the best case the code changes can be restricted to this project. The project can fix and hide specification inconsistencies and should provide consistent Java methods.
SessionApi
We can find all generated sub APIs in the bm.traccar.generated.api package.
For example we use the UsersApi and DevicesApi by setting the authentication,
sending a request and receive entities.
The ApiClient class holds the username and password.
When you call an API method, it will automatically construct and add the Authorization header.
This works perfectly for APIs that are stateless and check the basic auth header on every request.
The SessionApi is a special case as REST and HTTP are defined as a stateless protocols.
A session makes sense, if we log into the browser UI and keep it open.
For the traccar-api-client we don’t need to establish a session for REST interactions.
The sole purpose is to use the SessionApi to create a session
and retrieve a JSESSIONID (Java Session Id) to be used with a websocket connection.
You can make a POST request to the /api/session endpoint with the user’s email and password.
This will create a session and return a Session Cookie managed by Jetty.
This process is implemented in the ApiService and can be called with
String jSessionId = api.session.createSessionGetJsessionId(userMail, userPassword);
That’s all we need from the SessionApi and we will move on to the websocket implementation
with the returned JSESSIONID for the provided User.
When you connect to websocket endpoint /api/socket (Not REST endpoint /api/session!),
the server checks for the presence of a valid session cookie
to authorize the real-time data stream.
Traccar ApiAspect and ApiException
This API client implementation applies Aspect-Oriented Programming (AOP)
as the technique for handling exceptions in Spring Boot applications.
This way all exception handling happens in one central ApiAspect class
and avoids code duplication.
We apply AOP, since the Traccar REST API is a moving target. SubAPIs are added, methods and types are changed etc. With AOP we can grasp a general pattern that applies to all sub APIs, even the ones we haven’t implemented or Traccar will offer in future.
Generally any problem in the client software should through an ApiException
to take over responsibility and to provide useful messages for developers
and wrap the cause for deeper analysis.
As you can see in the bm.traccar.api.ApiAspect class
the @Pointcut includes all *Api classes and methods in :
@Pointcut("execution(public * bm.traccar.generated.api.*Api.*(..))")
and the Joinpoint is defined to only catch RestClientExceptions,
wrap them in an ApiException and throw it for the method execution:
public void afterThrowingApiMethod(JoinPoint joinPoint, RestClientException rcEx)
throws ApiException {
Note that the ApiException is a RuntimeException and
the caller is not forced by the compiler to catch it.
However you should handle it as good practice
and to ensure that you have a binding communication.
Spring AOP
invocation order
invoke **UsersApi.usersPost(..) ** <-------------+
invoke ApiClient.selectHeaderAccept(..) |
invoke ApiClient.selectHeaderContentType(..) |
invoke ApiClient.invokeAPI(..) |
Exception in ApiMethod: **UsersApi.usersPost(..) **
Traccar Scenario
For Real Time Development it makes sense to precisely define the RT environment to be observed and controlled.
In the context of a Tracking System we will simply define the environment as a (fixed) set of devices and call it a Scenario.
These devices will represent RealObjects (that we will develop eventually) in the Real World.
They can be many vehicles, some pedestrians, bicycles - if we think of a Traffic Application.
Scenario Entities
In conjunction with Traccar a scenario is composed of different sets of Traccar Entities.
The RealTimeModel (in the traccar-realtime-client project) will allow access to Traccar Users, Devices,
Drivers etc. to compose any scenario that fits your business.
This RealTimeModel or RealTimeManager or RealTimeStateManager reflects all objects
of one authenticated Traccar User and can only see what he is allowed to see.
This can be experienced and explored in the Traccar UI - as we proceed.
Scenario Properties
For a concrete development approach we will define
a Scenario simply as a (fixed) number of devices.
All other Traccar Entities are related to these devices somehow.
At development time we will create a complete Scenario from a scenario.properties file,
which defines all actors of the - for the time being: closed system.
|
Note
|
For convenience the scenario is placed in the actual project packages and not in the test packages. This makes it easier to be used to test higher level projects. |
On the long run there are endless ways to persist and load a scenario.
With json you can define a complete relational model to load
and bigger models could be retrieved from the database itself.
For compressed ER models
Googles Protocol Buffers could do the job,
as we have already practiced in the JeeTS project.
At dev time we will use the scenario.properties file to explore
Traccar’s Permission Management.
We’ll start with some simple initial rules to setup a Scenario:
-
scenario.propertiesdo not define any technical details on the Traccar Server.
These should remain in theapplication.propertiesand the server should then be prepared to load a Scenario. Less technical and more domain-driven. -
Each scenario must have (at least) one
User(account!) as the Scenario Manager.
This authenticatedUsermust be authorized to see the complete Scenario - vice versa his view defines the Scenario. The smallest scenario would be oneUserwith access to his ownDevice.
So let’s begin to fill the file with Users and Devices:
# basic scenario
## users
scenario.user[0].name=admin
scenario.user[0].password=admin
scenario.user[0].email=admin@scenario.com
scenario.user[0].administrator=true (1)
## devices
scenario.device[0].name=runner
scenario.device[0].uniqueId=10
scenario.device[0].model=ro
-
First
user[0]is the scenario manager, i.e.administrator
This scenario defines one administrator, one manager, two users
and four devices for a start.
ScenarioProperties
Now we can apply Spring Technology to load these properties
with the ScenarioProperties class
@Component
@ConfigurationProperties(prefix = "scenario", ignoreUnknownFields = false)
@PropertySource("classpath:scenario.properties")
public class ScenarioProperties { ...
We are using @ConfigurationProperties for Type-Safe Configuration
for managing multiple related properties.
The class groups all the related properties together,
is type-safe, less error-prone, and keeps your configuration clean.
You can inject the entire ScenarioProperties object into any other bean
(like a service or controller) using standard dependency injection.
The configuration is set in the ScenarioConfig class, which can be empty:
@Configuration
@EnableConfigurationProperties(ScenarioProperties.class)
@PropertySource("classpath:scenario.properties")
public class ScenarioConfig { /* empty */ }
And finally we unit test the auto wiring
public class ScenarioPropertiesTest {
@Autowired private ScenarioProperties scenarioProperties;
Now we can access the ScenarioProperties class to pick up the objects.
ScenarioLoader
Note that the property objects differ from the Traccar Entities.
bm.traccar.rt.scenario.ScenarioProperties.Device
bm.traccar.generated.model.dto.Device
So let’s code a little ETL loader to Extract the property objects, Transform them into Traccar Entities and Load (CRUD) these to the Traccar Server.
The ScenarioLoader reads the objects from the properties file
@Autowired private ScenarioProperties props;
and creates the Traccar Entities on the server via api.
@Autowired protected Api api;
Functionality and structure are straight forward.
Scenario Tests
Run the tests in test package bm.traccar.rt.scenario
to go into details and debugging.
In test package bm.traccar.rt
you will find a BaseReaTimeScenarioTest which sets up
the scenario.properties @BeforeAll testing.
It tears down the scenario and clears the server @AfterAll testing.
This is the base for the *IT tests in the package.
The ReaTimeScenarioIT sets up the scenario and
displays all Beans in the ApplicationContext.
Here we will find all classes of the bm-traccar branch
as you can see by the package names, i.e. application layers:
bm.traccar.invoke.ApiClient // traccar-openapitools-client
bm.traccar.generated.api.DevicesApi
.. and other *Apis
bm.traccar.api.ApiService // traccar-api-client
..
bm.traccar.rt.RealTimeController // traccar-rt-client
..
bm.traccar.ws.TraccarSessionManager // traccar-rt-client websocket
bm.traccar.ws.TraccarWebSocketRoute
Note that the bm.traccar.rt.RealTimeManager is not a Spring Bean
and is managed by the RealTimeContoller!
Traccar User Management
This is an ITest guide through Traccar’s User Management
Server Roles
Let’s have another look at the ScenarioLoaderIT.
@Before the tests are started the Traccar Docker Container is started. At this point the server is launched from its original distribution. This implies that there is no registered User at this point to take control. A hen and egg problem.
virtualAdmin
Therefor we have setup a virtualAdmin, which is predefined
in the traccar.xml file with
<entry key='web.serviceAccountToken'>VIRTUAL_ADMIN_ACCESS</entry>
then this value is matched in the application.properties
traccar.web.serviceAccountToken=VIRTUAL_ADMIN_ACCESS
and we can login to the server with
api.setBearerToken(virtualAdmin);
to create an Admin User defined in the prop file.
browser validation
You can debug the ITests and set a breakpoint
before the scenario is torn down.
Then you can login to the Traccar frontend via browser URL
http://localhost/?token=VIRTUAL_ADMIN_ACCESS
and check the status of users and devices.
Don’t forget to resume teardown!
User Account
The ROAF is about Tracking Devices being tracked by Traccar GTS.
Traccar is a multi client user system with a detailed permission structure
that we want to use.
We have setup the serviceAccount password to access the fresh server.
The first step is to create a Traccar Account represented by a User
- the admin.
admin
Admin - superuser with full unlimited access to the whole Traccar server.
The admin User is created on the server straight forward with minimal attributes
admin = api.getUsersApi().createUserWithCredentials(name, password, email, administrator);
This is the technical service administrator of the Traccar GTS,
is not part of the scenario and has no restrictions.
So we can leave the virtualAdmin behind and log in as admin
api.setBasicAuth(admin.getEmail(), props.getUser().get(0).password);
Note that we fetch the password from the user in the prop file, since passwords are not transfered from server.
Scenario Users
After we have setup the admin we will use this account
to setup the scenario
// continue as admin who is not part of the scenario!
api.setBasicAuth(admin.getEmail(), admin.getPassword());
createScenarioUsers();
createScenarioDevices();
Traccar’s User Management distiguishes three user roles
Traccar user management model supports use cases from simple individual user accounts
to large organizations with multiple managers, administrators and regular users.
manager
Manager - user with extended capabilities allowing them
to manage a subset of users and register new ones.
The difference between manager and regular user is in their user limit value.
A manager has the user limit not equals to 0.
We want one manager for each scenario.
Or a game master for each game.
He can observe the scenario and interfere like a referee,
even disqualify and take devices out of the scenario.
Again we can apply createUserWithCredentials to create a User
without admin rights and then we can update/upgrade the User
to become a manager:
// provide permissions
// 0: Cannot create any users/devices. regular user
// -1: Can create an unlimited number of users/devices. manager
// N: Can create up to N users/devices. manager
manager.setUserLimit(2);
manager.setDeviceLimit(3);
api.getUsersApi().updateUser(manager.getId(), manager);
Now the manager can log into the frontend
and create two Users and two Devices.
If he tries to add another one the frontend will report
Manager user limit reached.
If you’ll have a look at the @Test managerUserLimit() in
UsersApiIT
you can see how a - technical - admin creates a manager via REST.
Then this manager is authenticated and is used to create
three Users with manager.setUserLimit(2)!
Further analysis showed that all created Users belong to the users
marked as admin.
To setup a more complex scenario with individual rights
we need to work with the PermissionsApi.
Traccar Permissions
Permissions
Permissions = Link an Object to another Object
=> User = user account
http://localhost/api/users > all users as json
Administrators and managers can control permissions for their users from the Users menu in the settings. It is accessible from the Connections menu option in the users list.
Almost all entities in Traccar have associated permissions and can be linked to user accounts. It applies not only to devices, but also things like geofences, notifications etc.
Link an Object to another Object
Authorizations: BasicAuthApiKey Request Body schema: application/json required userId integer <int64> User id, can be only first parameter
deviceId integer <int64> Device id, can be first parameter or second only in combination with userId
groupId integer <int64> Group id, can be first parameter or second only in combination with userId
geofenceId integer <int64> Geofence id, can be second parameter only
notificationId integer <int64> Notification id, can be second parameter only
calendarId integer <int64> Calendar id, can be second parameter only and only in combination with userId
attributeId integer <int64> Computed attribute id, can be second parameter only
driverId integer <int64> Driver id, can be second parameter only
managedUserId integer <int64> User id, can be second parameter only and only in combination with userId
commandId integer <int64> Saved command id, can be second parameter only
Link an Object to another Object
userId deviceId userId groupId
userId managedUserId
userId calendarId
userId geofenceId deviceId geofenceId groupId geofenceId
userId notificationId deviceId notificationId groupId notificationId
userId attributeId deviceId attributeId groupId attributeId
userId driverId deviceId driverId groupId driverId
userId commandId deviceId commandId groupId commandId
Administrators and managers can control permissions for their users from the Users menu in the settings. It is accessible from the Connections menu option in the users list.
When an object is shared between several users, they have full access to it, including the ability to delete it. If you want to avoid it, you can duplicate instances so that each user has their own object. Duplicating a device with the same unique id is not possible, so Traccar provides a special device readonly user setting to restrict users for modifying devices.
Devices
Some entities can be linked to devices. For example, a geofence or a notification can be linked to a device. When linked, it means that it’s associated with the device, but it does not affect user permissions. To provide user access, it also needs to be linked to the user account.
For example, if a geofence is linked to a device, it means that the geofence events will be generated for this device.
Linking can be done from the settings. It is accessible from the Connections menu option in the devices list.
Groups
A group represent a group of devices. It is not possible to group any other entities, but you can link some entities to groups the same way you link them to devices. Same as with devices, linking to a group does not affect user permissions.
For example, if a geofence is linked to a group, it means that the geofence events will be generated for all devices in the group and subgroups.
Linking can be done from the settings. It is accessible from the Connections menu option in the groups list.
To add a device to a group, edit the device and select the Group under the Extra section. Groups can be nested. To add a group to another group, edit the group and select the parent Group under the Extra section.
setup scenario (later in BaseIT) - create admin > over all users (players) and (their) devices - create user/s > over his own device/s - create device/s for admin/user - login as admin > load all devices -> one MrX and two/three Detectives
upgrading traccar-api-client
The generation of the Java API Client takes place in the traccar-openapitools-client
as a completly independant process and therefor versioned one to one against Traccar.
The jar can be used in any project and does not require customization for this repo.
The versioned input
openapi.yaml
file is defined in
the pom file.
The project documentation also describes different
formats
to be aware of.
v1.1.17 - Traccar v6.10.0 to v6.12.2
After upgrading the Traccar version
<traccar.version>6.12.2</traccar.version>
in the parent pom, the traccar-openapitools-client should compile without further ado.
So you can actually compile the complete repo and wait for compilation problems in the traccar-api-client.
[ERROR] traccar-api-client/src/main/java/bm/traccar/api/impl/DevicesImpl.java:[40,22]
method devicesGet in class bm.traccar.generated.api.DevicesApi cannot be applied to given types;
required: Boolean, Integer, Integer, String, Boolean
found: <nulltype>, <nulltype>, <nulltype>, String
reason: actual and formal argument lists differ in length
[INFO] 1 error
When you open DevicesImpl.java the IDE provides another error message:
The method devicesGet(Boolean, Integer, Integer, String, Boolean) in the type DevicesApi is not applicable for the arguments (null, null, null, String )
You should counter check the differences directly in the Traccar repo:
v6.10.0 to v6.12.2
The new! javadoc tells us
Fetch a list of Devices Without any params, returns a list of the user's devices
List<Device> bm.traccar.generated.api.DevicesApi.devicesGet(
@Nullable Boolean all,
@Nullable Integer userId,
@Nullable Integer id,
@Nullable String uniqueId,
@Nullable Boolean excludeAttributes ) throws RestClientResponseException
Luckily we are still prototyping and avoiding bulk implementations and are not getting tempted to direct this to your CoPilot or other AI agent!
In order to test our methods implemented so far we have our Integration Test
in place: DevicesApiIT …
Long story short:
During prototyping we want to keep it as simple as possible.
For the time being we can simply ignore the new parameters (all being @Nullable)
and call the method with null values without any issues.
Therefor we simplify the main API interface method to
List<Device> getDevices(); // GET
and the implementation:
@Override
public List<Device> getDevices() {
return devicesApi.devicesGet(null, null, null, null, null);
}
The ITests will be used without parameters as a generic method.
We can still filter on client side and we can add getDevices
methods with descriptive names.
-
✓ done
If we’re lucky every new version is only a version bump with small changes.
It this case we can wittness a significant change in the API
with the addition of new parameters to existing endpoints and the addition of new endpoints.
No need to go into details here, since we implement new API methods on demand.