The gps-osmand-tracker is a Java Software to simulate
a standard hardware device that collects GPS locations, optionally buffer
and send them to a server asap.
Reporting should be configurable for time- and distance intervals,
change of direction etc.
Tracker Components
|
Note
|
Note that we are citing original texts from GPS Tracking with Java EE Components |
What exactly is a tracking device and what is its core functionality?
A tracker hardware is basically a combination of a GPS (processing) unit and a (GSM) communication unit. The GPS receives the location via satellite and the communication unit sends tracking messages to a server.
…
In the end every tracker should be able to send Coordinates, preferably WGS84 latitude and longitude and universal time, which the Server can use to localize the device.
Chapter 8.1
The main components are the GPS receiver and GSM transmitter chips for receiving and sending locations. Since both chips have to deal with a lot of information and technical challenges they are synchronized on a Controller Board. A Message Buffer serves as a local database to keep the best location results and wait for a good connection to transmit them to the Tracking System.
The fourth component, the Energy Source is vital for the hardware Tracker. And similar to a mobile phone the battery is the most space consuming component, but luckily is not needed for the software simulation.
A tracking hardware combines a GPS- and GSM unit on a Controller Board. The Controller evaluates sensor data to create event messages in the protocol format. NMEA data is constantly validated to provide the best position available. The messages can be sent whenever appropriate.
Figure 8.1
GPS receiver
A GPS chip (with antenna) acquires signals from orbiting satellites to calculate the location on Earth - a highly mathematical, trigonomic number cruncher. The location is continuously calculated by trilateration from (the best available) three satelites. Note that a fourth satelite is required to provide the precise time.
It is useful to understand that a GPS unit is primarily concerned
to deliver latitude, longitude and time.
The altitude is more of a side effect and not as reliable
due to geometric reasons.
This implies that the GPS unit primarily provides point information.
speed and bearing can be provided, but most Tracking Systems
calculate them from the previous to the current positon.
GSM transmitter
The GSM or any other network technology is basically the entry point of GPS data to the internet.
It can send the locations over the connected network (if available)
and can communicate via TCP and UDP low level protocols
and higher protocols, like http/s, on top.
This unit is also working all the time to get a good connection
over the air and transmitting whenever possible.
Message Buffer
As both GPS- and GSM units are busy all the time the Message Buffer can collect all valid and accurate locations from the GPS, while independantly pushing them to the server.
Communication Protocol
It is important to realize that a Tracking Device is not an application client
and the data transmission has nothing to do with the REST API.
It is simply some thing (Internet of Things, IoT) that sends information,
very often via proprietary protocols. A protocol is subdivided into sentences
to report different concerns. The most primitive GPS Protocol is NMEA 0183
which actually describes the primitives of GPS acquisition, like the satelite constellation.
The protocol is described in more detail in
The ROAF, Part II - Chapter 4.5 NAVSTAR GPS, 4.5.1 NMEA 0183
From the Tracker perspective the server is not the Traccar Platform, but simply a port to a Device Communication Server (DCS) which does the ETL work. The DCS is implemented with Netty Pipelines and the Decoder does the actual parsing targeting Traccar.
OsmAnd Protocol
Message Sentences combine a number of values that belong together.
A complete protocol defines the available attributes for an entire system - or scenario.
We chose the OsmAnd protocol because it is very simple and it is implemented
on the Traccar Server (for the Traccar Client App).
Or more general the Traccar Server offers a DCS
for this protocol on a configurable port (default 5055).
Technically "The OsmAnd protocol processes HTTP requests
with either query parameters or POST parameters." — and the protocol allows to add attributes!
Geo Coordinates
For Global Positioning will use the GeoTools library
as our interface to process GPS Positions,
for dealing with Digital Maps and Geographic Information Systems - GIS.
This way the Software is compatible to almost any geoengineering tool
and to components like an embedded h2GIS server. usefull!
Yet, in geographic processing the most important performance aspect is mathematical.
Imagine you need to compare thousands of distances to operate in a scenario.
To calculate the exact distance on the globe we have to use a projection system.
We will look at the performances differences by comparing the formulas
for a geographical versus a cartesian distance below.
The GeoTools library is tightly coupled with the
Java Topology Suite (JTS) library.
The Coordinate is a core class from the GeoTools / JTS library,
which is the standard geometry model GeoTools is built on.
Note that the Java Topology Suite is actually not working with geocoordinates
and optimized for trigonometric performance.
It is distinct from Point, which is a subclass of Geometry. Unlike objects of type Point (which contain additional information such as an envelope, a precision model, and spatial reference system information), a Coordinate only contains ordinate values and accessor methods.
Since the cartesian method is much faster we want to be able to ignore projection systems.
Therefore we are using the
Coordinate
to store the latitude, longitude and altitude with double precision.
It has many useful features like the usage 2d or 3d coordinate:
// 2D Koordinate (JTS) Coordinate coord2D = new Coordinate(10.0, 20.0); Coordinate coord3D = new Coordinate(10.0, 20.0, 500.0); // x, y, z
The GeneralDirectPosition is constructed the same way
// GeoTools Position (3D) GeneralDirectPosition pos3D = new GeneralDirectPosition(10.0, 20.0, 500.0);
but belongs to the more complex GeoTools Referencing-Framework and carries metadata of coordinate systems etc.
We can always transform the JTS coordinates to geocoordinates (at a cost).
With the GeoTools library you can tune the PrecisionModel
and the built-in GeodeticCalculator does not require geodetic expertise.
At this point it doesn’t seam like a big deal,
because we simply storing three double values in one Coordinate.
Let’s have a look at the formulas to get a sense when to use which.
Haversine Distance
The geographically calculation has to consider the globe’s radius, the curvature etc.
The Haversine Formula determines the great-circle distance between two points on a sphere:
-
d is the distance between the two points.
-
\(\phi\) is latitude, \(\lambda\) is longitude.
-
\(\Delta\phi = \phi_2 - \phi_1\)
-
\(\Delta\lambda = \lambda_2 - \lambda_1\)
-
\(R\) is the radius of the sphere (Earth’s mean radius is approx. 6,371 km).
|
Tip
|
Most programming languages provide trigonometric functions that require input in radians. |
When comparing distances their precise value does not matter, only relativity counts.
To calculate a distance in a cartesian plane we can also apply Pythagoras:
Pythagorean Distance
The Pythagorean Theorem is a fundamental principle in Euclidean geometry relating the three sides of a right triangle. The theorem states that in a right-angled triangle, the area of the square whose side is the hypotenuse (the side opposite the right angle) is equal to the sum of the areas of the squares on the other two sides.
Or simply speaking a and b are the orthogonal deltas of lat and lon and c is the distance,
i.e. length of the hypotenuse.
Calculating a comparable distance c is as simple as
By simply comparing this with the Haversine Formula you can sense the differences in performance. While the distance is simply calculating a length the real number crunching takes place when overlapping Geofences come into play. Note that geofence has many edges and each one has to be taken into account, yet should not slow down the real time scenario.
The bm Geodesy Standard
We don’t want to be distracted by reference systems.
The most conservative way to deal with the definition of a CoordinateReferenceSystem is not to.
Instead make use of an authority that provides complete definitions defined by a simple code.
Throughout this bm repository we will use identical geodetic formulas.
Then the distances from location 1 to location 2
and from location 2 to location 1 are guaranteed to be identical.
This is very important for synchronicity.
Check out the JTS feature list to get an idea of geometric operations.
Distance Example
With the GeoTools GeodeticCalculator we calculate the geodesic distance in meters
between two (lat,lon) GPS points in the standard WGS84 (EPSG:4326) system:
CoordinateReferenceSystem wgs84 = CRS.decode("EPSG:4326"); // (1)
GeodeticCalculator calculator = new GeodeticCalculator(wgs84); // (2)
calculator.setStartingGeographicPoint (lon1, lat1); // (3)
calculator.setDestinationGeographicPoint(lon2, lat2);
return calculator.getOrthodromicDistance(); // (4)
-
Define the Coordinate Reference System
-
Initialize the Geodetic Calculator
-
Set start- and end coordinates
-
Get the orthodromic distance (distance along the ellipsoid surface)
Implementation Path
In the long run we want to track every Thing (real physical object) acting in a scenario.
Since every message implies the devices uniqueId, one Message Broker
could operate as one single Tracker for a complete scenario.
So all simulated devices could send their messages off the same Tracker
and could be scaled and sudivided with a Message Broker like RabbitMQ, ActiveMQ etc.
We want to provide an independantly working GPS Tracker (simulation) for each of these things.
A single device can be scaled with SEDA to decompose messages for event driven components.
|
Note
|
This implementation might help you to choose a Tracker Device Type for your real time scenario. The chosen protocol is a easy as it gets and the creation process can serve as a guideline. |
The Model
We have chosen the OsmAnd protocol and defined the OsmAnd GPS Message (targeting Traccar).
That is the information and format the server accepts and understands.
We will use it as our Model to guide Tracker development.
The protocol defines all attributes exchanged with the GTS platform.
We could generate a Java Class from the JSON format listed on the
traccar osmand page.
But we want to go our own way and only use GPS platonic values
for an initial GPS Tracker.
We are primarily interested to transmit place & time
and will fix the parameters in a simple record to hold a single GPS snapshot:
package bm.gps;
public record MessageOsmand(
// must have
String id, // traccar device uniqueId
double lat,
double lon,
long timestamp,
// optional objects to allow null values
Double speed,
Double bearing,
Double altitude,
Double battery,
Double hdop) { }
A record is an immutable data class and much easier to handle than a POJO.
Record classes, which are a special kind of class, help to model plain data aggregates
with less ceremony than normal classes.
Record Classes
Note that the primitive values are used as the "must haves" for GPS Tracking. At a later stage they can be abstracted to a GPS info interface to introduce different trackers to the same scenario.
The other values are modelled as objects in order to make them optional. Currently the traccar osmand page offers 16 different values, which can be introduced while we are prototyping.
-
For example the
hdop, i.e. Horizontal dilution of precision is not relevant for us.
It describes the quality of a message and we are only interested in the result. -
On the other hand the
accuracycan be missused to create a circle on the map,
which might be interesting for certain scenarios.
No need for final decisions at this point.
Traccar also allows custom attributes,
which could be sneeked into a scenario, if needed.
model deviations
The above model is a minimal starting example.
A GPS Tracking Platform has to deal with a lot more attributes.
We will use the Traccar GTS, which is build on the following related Entities:
Attribute |
Calendar |
Command |
CommandType |
Device |
DeviceAccumulators |
Driver |
Event |
Geofence |
Group |
Maintenance |
Notification |
NotificationType |
Permission |
Position |
ReportStops |
ReportSummary |
ReportTrips |
Server |
Statistics |
User |
These are not the actual Entities of the Traccar ERM.
These are DTO classes generated from the Traccar REST API openapi.yml file.
You can find the classes in the bm.traccar.generated.model.dto package
and they are available in every traccar-client- implementation.
Let’s compare the Device class
and a json devices message sent via websocket:
class Device { {devices=[
id: 638 {id=638,
name: runner name=runner,
uniqueId: 10 uniqueId=10,
status: offline status=online,
disabled: false disabled=false,
lastUpdate: null lastUpdate=2026-02-20T16:15:30.791+00:00,
positionId: 0 positionId=0,
groupId: 0 groupId=0,
phone: null phone=null,
model: ro model=ro,
contact: null contact=null,
category: null category=null,
attributes: {} attributes={},
calendarId=0,
expirationTime=null
}
]}
Both messages have the devices uniqueId in common to refer to the same device.
Yet the two models deviate by the two attributes calendar and expirationTime.
Important information for ETL stuff like mapping.
The other attributes of our Tracker Model record
can be found in the Position class:
package bm.gps; bm.traccar.generated.model.dto;
public record MessageOsmand ( public class Position {
String id, deviceId : Long
double lat, latitude : BigDecimal
double lon, longitude : BigDecimal
long timestamp, fixTime : OffsetDateTime
Double speed, speed : BigDecimal
Double bearing, course : BigDecimal
Double altitude, altitude : BigDecimal
Double battery,
Double hdop) { }
geo precision
Another thing worth mentioning is the value- and type safety of decimals.
The openapi generator creates the Position class with BigDecimal types
private BigDecimal latitude, longitude, altitude;
The difference between BigDecimal and double is precision against performance.
For financial computation the precision,
i.e. the exact sequence of numbers after the point, is indisputable.
With double the decimal values are not always precise.
For example, there is no precise decimal representation of one Third.
Therefor some tricky algorithm with memory has to make sure 1 / 3 * 3 is exactly 1.
This should be kept in the back of our mind, since it does matter
when you want to know, if two points on a map are identical.
Or you want to find all identical points in the complete map data.
If the values are transfered with a string format like json or as URL parameters
they are actually converted into their literal value.
To stay on the save side comparisons should always be made with tolerance.
For the Tracker and the Message we will use double, standard for most geotools and -libs.
Requirements
First we collect the functional requirements for the core features of tracking,
a Minimum Viable Product we can build on.
We have looked at the main
Tracker Components
earlier to gather requirements for the implementation:
GPS Unit / receiver
-
✓ Acquire GPS position (latitude, longitude, altitude)
-
✓ UTC Time
-
❏ incoming Message Buffer here ?
(GSM) transmitter
-
✓ Transmit messages (primarily location data) to a remote server (any TCP, UDP protocols)
-
❏ configurable intervals e.g. every x seconds when moving, every y minutes when stationary etc.
-
✓ manage connection to server, reconnection
-
✓ outgoing Message Buffer
Controller
The main controller resides between receiving input and transmitting output. Most features of a Tracker can be implemented in it and it is the place to collect system information continuously.
-
❏ Store last known position (or short history)
-
❏ Fallback: store positions locally when no network → send burst when reconnected
-
❏ Motion detection → change reporting frequency when moving vs. stationary
-
❏ Determine basic movement data: speed, course/heading
-
❏ provide basic status events: power on, transmitting, low battery, error.
-
❏ Low-battery warning (send alert or change behavior)
-
❏ Geofencing support (enter/exit alerts when crossing virtual boundaries)
This list can provide a guideline for software @Components
and is sufficient to create a concrete implementation without surprises.
Message Buffer
-
❏ longterm nice2have: analyze incoming data in millisecond intervals for real time events
Primary Use Case
the (Java) application developer,
i.e. user of the gps-osmand-tracker-1.1.1x.jar
wants to create a message and send it.
fire & forget
Component Architecture
At this point it is important to prototype the components architecture as an reliable development guideline — and always subject to change. Prototyping is the first implementation to demo the proof of concept for single component to a complete framework. Main Technology- and Design Decision have to be made to fullfill the POC. And many design candidates and technologies are sorted out. This way the stack can be versioned with a bill of materials (BOM) over the complete repo to optimize transparency for (onboarding of business) coders.
implementation steps
-
❏ create a Springboot application to simulate a GPS Tracker hardware
-
❏ use the osmand protocol with Traccar ID
-
❏ use the OpenTools library for GPS coordinates and distance, bearing etc.
-
❏ setters for location and fixtime
-
❏ send messages via Camel Route
-
❏ store messages in a thread-safe queue, if server is unavailable, send later
Receiver
Actually the receiver, i.e. GPS Unit is already
completly defined with the MessageOsmand Model.
It specifies which attributes the Tracker can handle.
The data acquistion can be "implemented" with getters/setters of the Controller.
These will be connectors to simulations, playbacks and live tracking.
Energy and UTC time are sponsored by the runtime environment.
We can already check off some requirements :)
Controller
The Controller is the Tracker, i.e. TrackerOsmAnd core.
We have chosen Camel to integrate components
and a Camel rider starts software design with a local direct Endpoint:
// to Sender Component
from("direct:send-osmand")
From this point we will send messages to the Traccar server.
In integration terms it is a Producer Endpoint to send a fixed message.
On the other hand we want to leave all options to create messages.
This is achieved by using the Camel Producer to send messages
to "direct:send-osmand", now being a Consumer Endpoint.
For example you can create a regular Java method and instead of return
you end the method with sendBody. Camel will take care of the rest.
For the developer the message can be considered as sent.
// Tracker Component
public void send(MessageOsmand msg) {
// evaluations etc.
// synchronous at dev time
tracker.sendBody("direct:send-osmand", msg);
// more traffic
// tracker.asyncSendBody("direct:send-osmand", msg);
// even more traffic
// tracker.asyncSendBody("seda:send-osmand", msg);
}
Now we are set to start implementing with this Endpoint ..
-
as Consumer of a immutable message and
-
as Producer to send this message.
sounds easy,
'cause it is ;)
Sender
We now have Input and a Controller.
Next is the Output, the messaging/transport layer to the GTS.
The Transmitter is responsible to submit (and acknowledge) the messages to Traccar.
First we need to make sure the controllers call introduced above
// message to Sender
tracker.sendBody("direct:send-osmand", msg);
is intercepted by a transmitter, so we implement a Route with
TrackerSender extends RouteBuilder
which takes care of all the Spring and Camel wiring, autodetection etc.
The transmission route starts with
// send message to server
from("direct:send-osmand")
The route dynamically builds the server URL, manages http headers and properties
and sends the message to Traccar via Dynamic Endpoint .toD( host + parameters ).
load balancing
In the // Tracker listing above different types of sending a message are indicated.
This way the prototype leaves some room to scale implementations for a dedicated application.
Each send type can get its own method as we widen the Software for production.
Note that in the above sample we are using the routeId send-osmand-route as transmitter.
This implies that this very route is re/created in each tracker instance.
When Camel adds a route with the same routeId is will stop/replace the existing route.
We are interested to create an independant tracker to track an independant thing.
Therefore the initial implementation will respect the uniqueId inside the tracker implementation.
routeId("send-osmand-route-" + uniqueId)
from("direct:send-osmand-" + uniqueId).
Per-tracker routes increase Camel route count with resource growth.
For many trackers in a single application, we will consider
a single shared Transmitter route that handles any device
(messages carry device id) to avoid many dynamic routes.
The CamelContext provides full flexibility to manage all routes
and leaves room for load optimization - any time later.
Once the tracker beta version is running there is still room for a different setup.
In the long run this could be controlled with a switch to be introduced.
Message Buffer
A Message Queue is supposed to collect the message that can not be sent immediately.
Have a look at the
Device developed for Netty with JeeTS.
For this Device we created a loop
// All Messages are collected in a MessageQueue and
// the MessageLoop takes care of sending them whenever connectivity allows it.
private class SendMessageLoop implements Runnable {
which is wrapped around the
// Blocking queue to synchronize sending and callback.
private BlockingQueue<Object> callbackQueue = new LinkedBlockingQueue<Object>(1);
This was implemented for Demo purposes and can also be perceived
as a simple Endpoint implementation.
This time we want a robust and configurable Endpoint as a sender.
SEDA architecture
SEDA or staged event-driven architecture is a general EIP for software architecture.
SEDA refers to an approach to software architecture that decomposes a complex, event-driven architecture (EDA) into a set of stages connected by queues. It avoids the high overhead associated with thread-based concurrency models and decouples event and thread scheduling from application logic.
In general a SEDA controller is any mechanism that manages the consumption of resources
such as thread pool size, event queue size, scheduling, etc.
"seda:" Component
And, of course, Camel does provide a
SEDA component "seda:"
out of the box
The SEDA component provides asynchronous SEDA behavior,
so that messages are exchanged on a BlockingQueue and
consumers are invoked in a separate thread from the producer.
Creating a Tracker prototype with SEDA is a walk in the park:
"seda:send-osmand"
Yet SEDA is working in-memory with asynchronous processing within a single JVM to support for highly-concurrent traffic. Which means that our Tracker will loose all messages, if there is a crash.
This component does not implement any kind of persistence or recovery if the JVM terminates while messages are yet to be processed. If you need persistence, reliability or distributed SEDA, try using JMS.
We use the SEDA component to decouple your main process from volatile HTTP calls. We can "fire and forget" messages into an internal queue, let a separate thread pool handle network timeouts and server crashes.
One thing we can do with the "seda:" component is to activate
a Dead Letter Queue (DLQ) for development inspection of lost messages
and maybe to improve SEDA performance a bit.
As as example of a
errorHandler( deadLetterChannel(..
have a look at this JeeTS Device For sake of simplicity, we won’t introduce a DLQ - now.
Instead we will define the error handling inside the route with exponential backoff for the "reconnect" to happen:
onException(Exception.class)
.maximumRedeliveries(5) // Try 5 times
.redeliveryDelay(2000) // Wait 2 seconds between tries
.useExponentialBackOff()
.backOffMultiplier(2) // Double the wait time each failure (2s, 4s, 8s...)
.retryAttemptedLogLevel(LoggingLevel.WARN)
.handled(true) // Don't crash the whole route
.log("Failed to reach server after retries. Giving up on ${body}");
retry / backoff parameters can be tuned
or special handling for non-retriable HTTP codes can be added.
This is very helpful, since you don’t want to stress the server even more, when it cannot answer.
You can also configure Jitter (Collision Avoidance) when working with more Trackers.
Also note that Camel Components can be configured in the application.properties.
For SEDA it is camel.component.seda.*, like these examples:
# Configure SEDA camel.component.seda.queue-size=1000 camel.component.seda.concurrent-consumers=4 camel.component.seda.default-poll-timeout=1000 camel.component.seda.offer-timeout=0
For Custom Thread Pools you can manage the underlying thread pool more granularly,
you can also configure the Default Thread Profile:
# Configure the thread pool used by SEDA and other components camel.threadpool.pool-size=10 camel.threadpool.max-pool-size=20 camel.threadpool.max-queue-size=100
level up to Message Broker
Camel makes it easy to (or was made to) level up an existing architecture. The DSL structure remains almost identical. If required you can change SEDA to a full-blown Message Broker like ActiveMQ or RabbitMQ. By switching to JMS or AMQP, you gain persistence, better monitoring, and the ability to scale your consumers across multiple server instances.
One thing you should do to keep all options open
is to use a local "direct:" endpoint for your business logic.
Then you can switch from
from("direct:start").to("seda:send-osmand");
to ActiveMQ for Java/Camel ecosystems
from("direct:start").to("activemq:queue:send-osmand");
or to Use RabbitMQ for high performance, polyglot support etc.
from("direct:start").to("rabbitmq:gpsExchange?routingKey=send-osmand");
So we keep in mind that we can add Transaction Support (JTA), Persistence (JPA) and control Backpressure gracefully.
-
✓ Prepared for really large realtime scenarios.
About performance: after your application invokes
from("direct:start")
and instantly returns to your code in zero time.
Later we will use a realtime-client to confirm
that the message is processed on server side.
Disruption Test
The TrackerQueueDisruptionTest simulates the sending of three messages.
For the first message the server handles the test as wanted
18:18:25.229 tracker-4711 sending message 18:18:25.610 Server handling request OK 18:18:25.669 Sender: sent message to http://localhost:5055 via seda-send-osmand-route-4711
Then the server is disrupted before two more messages are sent.
Delivery fails and we can see the reattempts after two seconds:
18:18:25.766 Simulating server disruption (500 responses) 18:18:25.766 tracker-4711 sending message 18:18:25.767 tracker-4711 sending message 18:18:25.771 Server handling request ERROR 18:18:25.786 Failed delivery for (MessageId: 4E2F39A8CFF55AD-0000000000000003 18:18:26.229 Server handling request ERROR 18:18:26.234 Failed delivery for (MessageId: 4E2F39A8CFF55AD-0000000000000005 18:18:27.790 Server handling request ERROR 18:18:27.794 Failed delivery for (MessageId: 4E2F39A8CFF55AD-0000000000000003 18:18:28.239 Server handling request ERROR 18:18:28.244 Failed delivery for (MessageId: 4E2F39A8CFF55AD-0000000000000005
Finally the server is restored and the messages are sent successfully :)
18:18:30.768 Restoring server availability 18:18:31.798 Server handling request OK 18:18:31.799 Sender: sent message to http://localhost:5055 via seda-send-osmand-route-4711 18:18:32.249 Server handling request OK 18:18:32.251 Sender: sent message to http://localhost:5055 via seda-send-osmand-route-4711
Registration
Now how to use the Tracker in Spring Applications?
If a developer creates an actor of a scenario, some Java Object, and wants to track it he should be enabled to create a Tracker on the fly. There is no static configuration and it should happen at runtime.
lifecycle
With Spring and Camel the registration lifecycle register, unregister, reregister can be fully managed at runtime.
We register the tracker as a GenericBeanDefinition
so Spring automatically wires lifecycle and destruction callbacks.
TrackerRegistration registers a GenericBeanDefinition (constructor arguments uniqueId, osmandHost)
with DefaultListableBeanFactory and then pulls the fully-initialized bean from the context.
This makes Spring handle @PostConstruct and @PreDestroy automatically.
(Calling destroySingleton(beanName) removes the instance but the bean definition remains.)
On subsequent register attempts the code will see the existing bean definition and try to return/get the bean.
Removing the bean definition after destroying the singleton fully clears Spring’s state
for that bean name so registerTracker can create/register the bean again without conflicts.
With our components being Spring beans, we want to create and
register a new bean instance at runtime and then start its associated Camel route.
This is achieved using the ApplicationContextAware interface to handle the dynamic creation
and registration of a fully managed component.
With AutowireCapableBeanFactory the bean is created manually,
allowing Spring to handle all the necessary autowiring (like the ProducerTemplate and CamelContext).
usage
And this is how to use the gps-osmand-tracker-1.1.15.jar in your code:
@Autowired TrackerRegistration registrationService; (1)
void sendMessage() throws Exception {
// get uniqueId from application context
TrackerOsmAnd tracker = registrationService.registerTracker(uniqueId); (2)
MessageOsmand msg =
MessageOsmand.now(uniqueId, 52.0, 13.0, 10.0, 20.0, 30.0, null, null); (3)
tracker.send(msg); (4)
registrationService.unregisterTracker(uniqueId); (5)
...
-
provide tracker registration service
-
create and register tracker for uniqueId
-
create the immutable record
-
send, forget and continue application context
-
unregister tracker bean to clean up (and re-register)
Note that each Tracker bean is named with its Traccar identity
"tracker-" + uniqueId
This is a simple constraint to avoid multiple Trackers with the same id.
Actually the
IMEI
should be used for any hardware Tracker,
since it marks the unique hardware piece.
component wiring
We are using two frameworks to take care of @Autowired Spring components and Camel routes.
The general constellation for all trackers (to come) looks like this
Any number of Trackers can be created in the main application
(in this case a minimum @SpringBootApplication).
Each one can be looked up in the Registration to send Messages with the Tracker( uniqueId ).
sending sequence
sending a GPS snapshot
Register a number of Trackers in your application and send messages whenever appropriate. The sending method returns to the application instantly, while the Sender takes care of sending all messages until accepted by the Tracking Server.