Compare commits

...

3 Commits

Author SHA1 Message Date
trifonovt ce81bfe868 Ignore and untrack eventhub ingestion log 2026-06-10 17:45:26 +02:00
trifonovt 08b3cbf073 Fix VU activity date extraction from downloadTime 2026-06-10 17:45:26 +02:00
trifonovt 8a106c02c5 Restore tachographfilesession changes from 2f4ffc2 2026-06-10 17:40:33 +02:00
17 changed files with 648 additions and 303 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ target/
.project
.settings/
.DS_Store
logs/eventhub-ingestion-service.log

View File

@ -1,207 +0,0 @@
2026-05-12T11:15:13.519+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-8' version 9.0.0
2026-05-12T11:15:14.119+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-8'
2026-05-12T11:15:14.122+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-9' version 9.0.0
2026-05-12T11:15:14.293+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-9'
2026-05-12T11:15:14.299+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-operating-period-2' version 9.0.0
2026-05-12T11:15:14.348+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-operating-period-2'
2026-05-12T11:15:14.353+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-10' version 9.0.0
2026-05-12T11:15:14.391+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-10'
2026-05-12T11:15:14.395+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-11' version 9.0.0
2026-05-12T11:15:14.438+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-11'
2026-05-12T11:15:14.440+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-12' version 9.0.0
2026-05-12T11:15:14.481+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-12'
2026-05-12T11:15:14.484+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-13' version 9.0.0
2026-05-12T11:15:14.526+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-13'
2026-05-12T11:15:14.528+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-14' version 9.0.0
2026-05-12T11:15:14.568+02:00 INFO 12264 --- [http-nio-8085-exec-5] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-14'
2026-05-12T11:15:14.569+02:00 INFO 12264 --- [http-nio-8085-exec-5] a.p.e.e.s.EsperOperatingPeriodEvaluationService : Esper operating-period evaluation tenant=Procon driverId=026598bf-002d-460d-8b06-ccbd05fb46b7 requestedFrom=2026-04-01T00:00Z requestedTo=2026-05-01T00:00Z loadedFrom=2026-03-31T00:00Z loadedTo=2026-05-02T00:00Z unknownMode=AS_BREAK_REST engineMode=STREAM_COLLECTOR rawEvents=708 cardRawEvents=354 vuRawEvents=354 cardIntervals=176 vuIntervals=168 resolvedKnownIntervals=176 evaluationIntervals=113 periodizedIntervals=113 mergedIntervals=113 nonDrivingIntervals=31 operatingPeriods=5 timingsMs={{dbRetrieve=347, cardIntervalEsper=939, vuIntervalEsper=174, vuGapFill=2, synthUnknown=1, periodizeEsper=51, merge=1, nonDriving=0, total=1738}}
2026-05-12T11:16:09.058+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.s.boot.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2026-05-12T11:16:09.272+02:00 INFO 12264 --- [tomcat-shutdown] o.s.boot.tomcat.GracefulShutdown : Graceful shutdown complete
2026-05-12T11:16:09.310+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) is shutting down (timeout:45s)
2026-05-12T11:16:11.015+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Routes stopped (total:10)
2026-05-12T11:16:11.016+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped yellowfox-d8-import-start-route (direct://yellowfox-d8-import-start)
2026-05-12T11:16:11.016+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped yellowfox-d8-booking-input-route (direct://yellowfox-d8-booking-input)
2026-05-12T11:16:11.144+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped tachograph-import-start-route (direct://tachograph-import-start)
2026-05-12T11:16:11.145+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped tachograph-activity-input-route (direct://tachograph-activity-input)
2026-05-12T11:16:11.146+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped telematics-position-input-route (direct://telematics-position-input)
2026-05-12T11:16:11.147+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-manual-input-route (direct://eventhub-manual-input)
2026-05-12T11:16:11.149+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-package-input-route (direct://eventhub-package-input)
2026-05-12T11:16:11.149+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-direct-batch-persist-route (direct://eventhub-batch-persist-input)
2026-05-12T11:16:11.149+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-batch-and-persist-route (seda://eventhub-batch-input)
2026-05-12T11:16:11.149+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-normalized-input-route (direct://eventhub-normalized-input)
2026-05-12T11:16:11.352+02:00 INFO 12264 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) shutdown in 2s8ms (uptime:18h55m)
2026-05-12T11:16:11.582+02:00 INFO 12264 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2026-05-12T11:16:11.614+02:00 INFO 12264 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2026-05-12T11:23:05.801+02:00 INFO 9580 --- [background-preinit] o.h.validator.internal.util.Version : HV000001: Hibernate Validator 9.0.1.Final
2026-05-12T11:23:06.558+02:00 INFO 9580 --- [main] a.p.e.EventHubIngestionApplication : Starting EventHubIngestionApplication using Java 21.0.2 with PID 9580 (C:\Work\java\gite\eventhub-spring-boot\eventhub-spring-boot\target\classes started by Parzival in C:\Work\java\gite\eventhub-spring-boot\eventhub-spring-boot)
2026-05-12T11:23:06.566+02:00 INFO 9580 --- [main] a.p.e.EventHubIngestionApplication : No active profile set, falling back to 1 default profile: "default"
2026-05-12T11:23:11.900+02:00 INFO 9580 --- [main] o.s.boot.tomcat.TomcatWebServer : Tomcat initialized with port 8085 (http)
2026-05-12T11:23:11.930+02:00 INFO 9580 --- [main] o.a.coyote.http11.Http11NioProtocol : Initializing ProtocolHandler ["http-nio-8085"]
2026-05-12T11:23:11.933+02:00 INFO 9580 --- [main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-05-12T11:23:11.933+02:00 INFO 9580 --- [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/11.0.21]
2026-05-12T11:23:12.061+02:00 INFO 9580 --- [main] o.s.b.w.c.s.WebApplicationContextInitializer : Root WebApplicationContext: initialization completed in 5194 ms
2026-05-12T11:23:15.477+02:00 INFO 9580 --- [main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 4 endpoints beneath base path '/actuator'
2026-05-12T11:23:15.639+02:00 INFO 9580 --- [main] o.a.coyote.http11.Http11NioProtocol : Starting ProtocolHandler ["http-nio-8085"]
2026-05-12T11:23:15.673+02:00 INFO 9580 --- [main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 8085 (http) with context path '/'
2026-05-12T11:23:20.172+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) is starting
2026-05-12T11:23:20.203+02:00 INFO 9580 --- [main] o.a.c.p.aggregate.AggregateProcessor : Defaulting to MemoryAggregationRepository
2026-05-12T11:23:20.204+02:00 INFO 9580 --- [main] o.a.c.p.aggregate.AggregateProcessor : Using CompletionTimeout to trigger after 5000 millis of inactivity.
2026-05-12T11:23:20.276+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Routes startup (total:10)
2026-05-12T11:23:20.277+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-normalized-input-route (direct://eventhub-normalized-input)
2026-05-12T11:23:20.279+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-batch-and-persist-route (seda://eventhub-batch-input)
2026-05-12T11:23:20.281+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-direct-batch-persist-route (direct://eventhub-batch-persist-input)
2026-05-12T11:23:20.281+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-package-input-route (direct://eventhub-package-input)
2026-05-12T11:23:20.282+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-manual-input-route (direct://eventhub-manual-input)
2026-05-12T11:23:20.282+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started telematics-position-input-route (direct://telematics-position-input)
2026-05-12T11:23:20.283+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started tachograph-activity-input-route (direct://tachograph-activity-input)
2026-05-12T11:23:20.284+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started tachograph-import-start-route (direct://tachograph-import-start)
2026-05-12T11:23:20.286+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started yellowfox-d8-booking-input-route (direct://yellowfox-d8-booking-input)
2026-05-12T11:23:20.286+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started yellowfox-d8-import-start-route (direct://yellowfox-d8-import-start)
2026-05-12T11:23:20.286+02:00 INFO 9580 --- [main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) started in 110ms (build:0ms init:0ms start:110ms boot:7s769ms)
2026-05-12T11:23:20.336+02:00 INFO 9580 --- [main] a.p.e.EventHubIngestionApplication : Started EventHubIngestionApplication in 18.093 seconds (process running for 22.74)
2026-05-12T11:23:30.575+02:00 INFO 9580 --- [RMI TCP Connection(5)-169.254.53.84] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2026-05-12T11:23:30.576+02:00 INFO 9580 --- [RMI TCP Connection(5)-169.254.53.84] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2026-05-12T11:23:30.590+02:00 INFO 9580 --- [RMI TCP Connection(5)-169.254.53.84] o.s.web.servlet.DispatcherServlet : Completed initialization in 13 ms
2026-05-12T11:23:35.926+02:00 INFO 9580 --- [RMI TCP Connection(3)-169.254.53.84] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-05-12T11:23:36.395+02:00 INFO 9580 --- [RMI TCP Connection(3)-169.254.53.84] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@1a7ba505
2026-05-12T11:23:36.417+02:00 INFO 9580 --- [RMI TCP Connection(3)-169.254.53.84] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-05-12T11:23:56.009+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-1' version 9.0.0
2026-05-12T11:23:58.662+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-1'
2026-05-12T11:23:58.675+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-2' version 9.0.0
2026-05-12T11:23:59.023+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-2'
2026-05-12T11:23:59.045+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-operating-period-1' version 9.0.0
2026-05-12T11:23:59.127+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-operating-period-1'
2026-05-12T11:23:59.150+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-3' version 9.0.0
2026-05-12T11:23:59.212+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-3'
2026-05-12T11:23:59.216+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-4' version 9.0.0
2026-05-12T11:23:59.297+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-4'
2026-05-12T11:23:59.301+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-5' version 9.0.0
2026-05-12T11:23:59.357+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-5'
2026-05-12T11:23:59.360+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-6' version 9.0.0
2026-05-12T11:23:59.409+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-6'
2026-05-12T11:23:59.412+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-7' version 9.0.0
2026-05-12T11:23:59.464+02:00 INFO 9580 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-7'
2026-05-12T11:23:59.464+02:00 INFO 9580 --- [http-nio-8085-exec-2] a.p.e.e.s.EsperOperatingPeriodEvaluationService : Esper operating-period evaluation tenant=Procon driverId=026598bf-002d-460d-8b06-ccbd05fb46b7 requestedFrom=2026-04-01T00:00Z requestedTo=2026-05-01T00:00Z loadedFrom=2026-03-31T00:00Z loadedTo=2026-05-02T00:00Z unknownMode=AS_BREAK_REST engineMode=STREAM_COLLECTOR rawEvents=708 cardRawEvents=354 vuRawEvents=354 cardIntervals=176 vuIntervals=168 resolvedKnownIntervals=176 evaluationIntervals=113 periodizedIntervals=113 mergedIntervals=113 nonDrivingIntervals=31 operatingPeriods=5 timingsMs={{dbRetrieve=411, cardIntervalEsper=2793, vuIntervalEsper=355, vuGapFill=9, synthUnknown=4, periodizeEsper=93, merge=3, nonDriving=2, total=4001}}
2026-05-12T11:24:58.174+02:00 WARN 9580 --- [HikariPool-1:housekeeper] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=49s488ms454µs500ns).
2026-05-12T11:26:51.871+02:00 WARN 9580 --- [HikariPool-1:housekeeper] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=1m9s537ms744µs800ns).
2026-05-12T11:29:00.780+02:00 WARN 9580 --- [HikariPool-1:housekeeper] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=2m8s908ms981µs900ns).
2026-05-12T11:29:00.971+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.s.boot.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2026-05-12T11:29:01.419+02:00 INFO 9580 --- [tomcat-shutdown] o.s.boot.tomcat.GracefulShutdown : Graceful shutdown complete
2026-05-12T11:29:01.425+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) is shutting down (timeout:45s)
2026-05-12T11:29:01.857+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Routes stopped (total:10)
2026-05-12T11:29:01.858+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped yellowfox-d8-import-start-route (direct://yellowfox-d8-import-start)
2026-05-12T11:29:01.858+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped yellowfox-d8-booking-input-route (direct://yellowfox-d8-booking-input)
2026-05-12T11:29:01.858+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped tachograph-import-start-route (direct://tachograph-import-start)
2026-05-12T11:29:01.858+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped tachograph-activity-input-route (direct://tachograph-activity-input)
2026-05-12T11:29:01.858+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped telematics-position-input-route (direct://telematics-position-input)
2026-05-12T11:29:01.858+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-manual-input-route (direct://eventhub-manual-input)
2026-05-12T11:29:01.860+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-package-input-route (direct://eventhub-package-input)
2026-05-12T11:29:01.863+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-direct-batch-persist-route (direct://eventhub-batch-persist-input)
2026-05-12T11:29:01.863+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-batch-and-persist-route (seda://eventhub-batch-input)
2026-05-12T11:29:01.863+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-normalized-input-route (direct://eventhub-normalized-input)
2026-05-12T11:29:01.885+02:00 INFO 9580 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) shutdown in 459ms (uptime:5m41s)
2026-05-12T11:29:01.998+02:00 INFO 9580 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2026-05-12T11:29:02.548+02:00 INFO 9580 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2026-05-12T11:47:04.173+02:00 INFO 29032 --- [background-preinit] o.h.validator.internal.util.Version : HV000001: Hibernate Validator 9.0.1.Final
2026-05-12T11:47:04.534+02:00 INFO 29032 --- [main] a.p.e.EventHubIngestionApplication : Starting EventHubIngestionApplication using Java 21.0.2 with PID 29032 (C:\Work\java\gite\eventhub-spring-boot\eventhub-spring-boot\target\classes started by Parzival in C:\Work\java\gite\eventhub-spring-boot\eventhub-spring-boot)
2026-05-12T11:47:04.536+02:00 INFO 29032 --- [main] a.p.e.EventHubIngestionApplication : No active profile set, falling back to 1 default profile: "default"
2026-05-12T11:47:08.931+02:00 INFO 29032 --- [main] o.s.boot.tomcat.TomcatWebServer : Tomcat initialized with port 8085 (http)
2026-05-12T11:47:08.966+02:00 INFO 29032 --- [main] o.a.coyote.http11.Http11NioProtocol : Initializing ProtocolHandler ["http-nio-8085"]
2026-05-12T11:47:08.970+02:00 INFO 29032 --- [main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-05-12T11:47:08.970+02:00 INFO 29032 --- [main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/11.0.21]
2026-05-12T11:47:09.120+02:00 INFO 29032 --- [main] o.s.b.w.c.s.WebApplicationContextInitializer : Root WebApplicationContext: initialization completed in 4466 ms
2026-05-12T11:47:12.877+02:00 INFO 29032 --- [main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 4 endpoints beneath base path '/actuator'
2026-05-12T11:47:13.064+02:00 INFO 29032 --- [main] o.a.coyote.http11.Http11NioProtocol : Starting ProtocolHandler ["http-nio-8085"]
2026-05-12T11:47:13.108+02:00 INFO 29032 --- [main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 8085 (http) with context path '/'
2026-05-12T11:47:17.510+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) is starting
2026-05-12T11:47:17.593+02:00 INFO 29032 --- [main] o.a.c.p.aggregate.AggregateProcessor : Defaulting to MemoryAggregationRepository
2026-05-12T11:47:17.600+02:00 INFO 29032 --- [main] o.a.c.p.aggregate.AggregateProcessor : Using CompletionTimeout to trigger after 5000 millis of inactivity.
2026-05-12T11:47:17.646+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Routes startup (total:10)
2026-05-12T11:47:17.646+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-normalized-input-route (direct://eventhub-normalized-input)
2026-05-12T11:47:17.646+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-batch-and-persist-route (seda://eventhub-batch-input)
2026-05-12T11:47:17.646+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-direct-batch-persist-route (direct://eventhub-batch-persist-input)
2026-05-12T11:47:17.646+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-package-input-route (direct://eventhub-package-input)
2026-05-12T11:47:17.647+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started eventhub-manual-input-route (direct://eventhub-manual-input)
2026-05-12T11:47:17.647+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started telematics-position-input-route (direct://telematics-position-input)
2026-05-12T11:47:17.648+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started tachograph-activity-input-route (direct://tachograph-activity-input)
2026-05-12T11:47:17.648+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started tachograph-import-start-route (direct://tachograph-import-start)
2026-05-12T11:47:17.648+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started yellowfox-d8-booking-input-route (direct://yellowfox-d8-booking-input)
2026-05-12T11:47:17.648+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Started yellowfox-d8-import-start-route (direct://yellowfox-d8-import-start)
2026-05-12T11:47:17.649+02:00 INFO 29032 --- [main] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) started in 137ms (build:0ms init:0ms start:137ms boot:7s937ms)
2026-05-12T11:47:17.672+02:00 INFO 29032 --- [main] a.p.e.EventHubIngestionApplication : Started EventHubIngestionApplication in 15.724 seconds (process running for 19.226)
2026-05-12T11:47:22.629+02:00 INFO 29032 --- [RMI TCP Connection(8)-169.254.53.84] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2026-05-12T11:47:22.629+02:00 INFO 29032 --- [RMI TCP Connection(8)-169.254.53.84] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2026-05-12T11:47:22.637+02:00 INFO 29032 --- [RMI TCP Connection(8)-169.254.53.84] o.s.web.servlet.DispatcherServlet : Completed initialization in 7 ms
2026-05-12T11:47:28.596+02:00 INFO 29032 --- [RMI TCP Connection(6)-169.254.53.84] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2026-05-12T11:47:29.328+02:00 INFO 29032 --- [RMI TCP Connection(6)-169.254.53.84] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@40294cec
2026-05-12T11:47:29.337+02:00 INFO 29032 --- [RMI TCP Connection(6)-169.254.53.84] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2026-05-12T11:47:47.390+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-1' version 9.0.0
2026-05-12T11:47:50.506+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-1'
2026-05-12T11:47:50.516+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-2' version 9.0.0
2026-05-12T11:47:50.761+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-2'
2026-05-12T11:47:50.785+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-operating-period-1' version 9.0.0
2026-05-12T11:47:50.874+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-operating-period-1'
2026-05-12T11:47:50.896+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-3' version 9.0.0
2026-05-12T11:47:50.961+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-3'
2026-05-12T11:47:50.968+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-4' version 9.0.0
2026-05-12T11:47:51.069+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-4'
2026-05-12T11:47:51.072+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-5' version 9.0.0
2026-05-12T11:47:51.143+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-5'
2026-05-12T11:47:51.148+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-6' version 9.0.0
2026-05-12T11:47:51.233+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-6'
2026-05-12T11:47:51.238+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-7' version 9.0.0
2026-05-12T11:47:51.328+02:00 INFO 29032 --- [http-nio-8085-exec-2] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-7'
2026-05-12T11:47:51.331+02:00 INFO 29032 --- [http-nio-8085-exec-2] a.p.e.e.s.EsperOperatingPeriodEvaluationService : Esper operating-period evaluation tenant=Procon driverId=026598bf-002d-460d-8b06-ccbd05fb46b7 requestedFrom=2026-04-01T00:00Z requestedTo=2026-05-01T00:00Z loadedFrom=2026-03-31T00:00Z loadedTo=2026-05-02T00:00Z sourceSelectionMode=MIXED unknownMode=AS_BREAK_REST engineMode=STREAM_COLLECTOR rawEvents=708 cardRawEvents=354 vuRawEvents=354 cardIntervals=176 vuIntervals=168 resolvedKnownIntervals=176 evaluationIntervals=113 periodizedIntervals=113 mergedIntervals=113 nonDrivingIntervals=31 operatingPeriods=5 timingsMs={{dbRetrieve=636, cardIntervalEsper=3333, vuIntervalEsper=251, vuGapFill=13, synthUnknown=5, periodizeEsper=96, merge=3, nonDriving=2, total=4792}}
2026-05-12T11:50:30.990+02:00 WARN 29032 --- [HikariPool-1:housekeeper] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=2m28s615ms199µs300ns).
2026-05-12T11:50:42.829+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-8' version 9.0.0
2026-05-12T11:50:43.060+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-8'
2026-05-12T11:50:43.065+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-9' version 9.0.0
2026-05-12T11:50:43.248+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-9'
2026-05-12T11:50:43.257+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-operating-period-2' version 9.0.0
2026-05-12T11:50:43.310+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-operating-period-2'
2026-05-12T11:50:43.314+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-10' version 9.0.0
2026-05-12T11:50:43.364+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-10'
2026-05-12T11:50:43.368+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-11' version 9.0.0
2026-05-12T11:50:43.418+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-11'
2026-05-12T11:50:43.421+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-12' version 9.0.0
2026-05-12T11:50:43.471+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-12'
2026-05-12T11:50:43.474+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-13' version 9.0.0
2026-05-12T11:50:43.537+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-13'
2026-05-12T11:50:43.541+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-14' version 9.0.0
2026-05-12T11:50:43.590+02:00 INFO 29032 --- [http-nio-8085-exec-4] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-14'
2026-05-12T11:50:43.590+02:00 INFO 29032 --- [http-nio-8085-exec-4] a.p.e.e.s.EsperOperatingPeriodEvaluationService : Esper operating-period evaluation tenant=Procon driverId=026598bf-002d-460d-8b06-ccbd05fb46b7 requestedFrom=2026-04-01T00:00Z requestedTo=2026-05-01T00:00Z loadedFrom=2026-03-31T00:00Z loadedTo=2026-05-02T00:00Z sourceSelectionMode=MIXED unknownMode=AS_BREAK_REST engineMode=STREAM_COLLECTOR rawEvents=708 cardRawEvents=354 vuRawEvents=354 cardIntervals=176 vuIntervals=168 resolvedKnownIntervals=176 evaluationIntervals=113 periodizedIntervals=113 mergedIntervals=113 nonDrivingIntervals=31 operatingPeriods=5 timingsMs={{dbRetrieve=25, cardIntervalEsper=236, vuIntervalEsper=189, vuGapFill=3, synthUnknown=2, periodizeEsper=56, merge=1, nonDriving=0, total=790}}
2026-05-12T11:51:45.110+02:00 WARN 29032 --- [HikariPool-1:housekeeper] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=1m14s121ms587µs300ns).
2026-05-12T11:52:29.604+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-15' version 9.0.0
2026-05-12T11:52:29.671+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.s.boot.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2026-05-12T11:52:29.787+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-15'
2026-05-12T11:52:29.789+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-16' version 9.0.0
2026-05-12T11:52:29.991+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-16'
2026-05-12T11:52:29.995+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-operating-period-3' version 9.0.0
2026-05-12T11:52:30.034+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-operating-period-3'
2026-05-12T11:52:30.038+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-17' version 9.0.0
2026-05-12T11:52:30.072+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-17'
2026-05-12T11:52:30.073+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-18' version 9.0.0
2026-05-12T11:52:30.102+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-18'
2026-05-12T11:52:30.104+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-19' version 9.0.0
2026-05-12T11:52:30.130+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-19'
2026-05-12T11:52:30.132+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-20' version 9.0.0
2026-05-12T11:52:30.171+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-20'
2026-05-12T11:52:30.176+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Initializing runtime URI 'eventhub-esper-poc-21' version 9.0.0
2026-05-12T11:52:30.406+02:00 INFO 29032 --- [http-nio-8085-exec-7] c.e.e.r.i.kernel.service.EPRuntimeImpl : Destroying runtime URI 'eventhub-esper-poc-21'
2026-05-12T11:52:30.408+02:00 INFO 29032 --- [http-nio-8085-exec-7] a.p.e.e.s.EsperOperatingPeriodEvaluationService : Esper operating-period evaluation tenant=Procon driverId=026598bf-002d-460d-8b06-ccbd05fb46b7 requestedFrom=2026-04-01T00:00Z requestedTo=2026-05-01T00:00Z loadedFrom=2026-03-31T00:00Z loadedTo=2026-05-02T00:00Z sourceSelectionMode=MIXED unknownMode=AS_BREAK_REST engineMode=STREAM_COLLECTOR rawEvents=708 cardRawEvents=354 vuRawEvents=354 cardIntervals=176 vuIntervals=168 resolvedKnownIntervals=176 evaluationIntervals=113 periodizedIntervals=113 mergedIntervals=113 nonDrivingIntervals=31 operatingPeriods=5 timingsMs={{dbRetrieve=39, cardIntervalEsper=186, vuIntervalEsper=203, vuGapFill=2, synthUnknown=1, periodizeEsper=41, merge=0, nonDriving=0, total=845}}
2026-05-12T11:52:30.449+02:00 INFO 29032 --- [tomcat-shutdown] o.s.boot.tomcat.GracefulShutdown : Graceful shutdown complete
2026-05-12T11:52:30.458+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) is shutting down (timeout:45s)
2026-05-12T11:52:30.590+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Routes stopped (total:10)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped yellowfox-d8-import-start-route (direct://yellowfox-d8-import-start)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped yellowfox-d8-booking-input-route (direct://yellowfox-d8-booking-input)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped tachograph-import-start-route (direct://tachograph-import-start)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped tachograph-activity-input-route (direct://tachograph-activity-input)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped telematics-position-input-route (direct://telematics-position-input)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-manual-input-route (direct://eventhub-manual-input)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-package-input-route (direct://eventhub-package-input)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-direct-batch-persist-route (direct://eventhub-batch-persist-input)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-batch-and-persist-route (seda://eventhub-batch-input)
2026-05-12T11:52:30.591+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Stopped eventhub-normalized-input-route (direct://eventhub-normalized-input)
2026-05-12T11:52:30.599+02:00 INFO 29032 --- [SpringApplicationShutdownHook] o.a.c.impl.engine.AbstractCamelContext : Apache Camel 4.18.2 (camel-1) shutdown in 140ms (uptime:5m13s)
2026-05-12T11:52:30.611+02:00 INFO 29032 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2026-05-12T11:52:30.615+02:00 INFO 29032 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.

View File

@ -11,6 +11,8 @@ import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDri
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionProcessingService;
import at.procon.eventhub.tachographfilesession.service.TachographFileSessionService;
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowResponse;
import jakarta.servlet.http.HttpSession;
import java.util.UUID;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -44,10 +46,21 @@ public class TachographFileSessionController {
@RequestParam("file") MultipartFile file,
@RequestParam(required = false) String tenantKey,
@RequestParam(required = false) String sourceInstanceKey,
@RequestParam(required = false) String sessionLabel
@RequestParam(required = false) String sessionLabel,
HttpSession webSession
) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(service.createSession(file, tenantKey, sourceInstanceKey, sessionLabel));
.body(service.createSession(file, tenantKey, sourceInstanceKey, sessionLabel, webSession));
}
@PostMapping("/workflow/reset")
public ResponseEntity<TachographUploadWorkflowResponse> resetUploadWorkflow(HttpSession webSession) {
return ResponseEntity.ok(service.resetUploadWorkflow(webSession));
}
@GetMapping("/workflow")
public ResponseEntity<TachographUploadWorkflowResponse> getUploadWorkflow(HttpSession webSession) {
return ResponseEntity.ok(service.getUploadWorkflow(webSession));
}
@GetMapping("/{sessionId}")

View File

@ -1,6 +1,12 @@
package at.procon.eventhub.tachographfilesession.dto;
public record CreateTachographFileSessionResponse(
TachographFileSessionSummaryDto session
TachographFileSessionSummaryDto session,
TachographUploadWorkflowDto workflow,
TachographCompositeSessionSummaryDto compositeSession
) {
public CreateTachographFileSessionResponse(TachographFileSessionSummaryDto session) {
this(session, null, null);
}
}

View File

@ -0,0 +1,13 @@
package at.procon.eventhub.tachographfilesession.dto;
import java.util.List;
import java.util.UUID;
public record TachographUploadWorkflowDto(
boolean driverCardUploaded,
UUID driverCardSessionId,
List<UUID> uploadedSessionIds,
List<UUID> vehicleUnitSessionIds,
UUID compositeSessionId
) {
}

View File

@ -0,0 +1,6 @@
package at.procon.eventhub.tachographfilesession.dto;
public record TachographUploadWorkflowResponse(
TachographUploadWorkflowDto workflow
) {
}

View File

@ -28,10 +28,8 @@ public class LegalRequirementsClient {
public LegalRequirementsUploadResult uploadTachographFile(byte[] fileBytes, String fileName) {
EventHubProperties.LegalRequirements config = properties.getTachographFileSession().getLegalRequirements();
HttpClient client = HttpClient.newBuilder()
.connectTimeout(config.getConnectTimeout())
.cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ALL))
.build();
CookieManager cookieManager = newCookieManager();
HttpClient client = httpClient(config, cookieManager);
try {
String dataPackageId = uploadDataPackage(client, config, fileBytes, fileName);
String xml = downloadXml(client, config, dataPackageId);
@ -43,6 +41,23 @@ public class LegalRequirementsClient {
}
}
public LegalRequirementsUploadResult uploadTachographFile(CookieManager cookieManager, byte[] fileBytes, String fileName) {
EventHubProperties.LegalRequirements config = properties.getTachographFileSession().getLegalRequirements();
HttpClient client = httpClient(config, cookieManager);
String dataPackageId = uploadDataPackage(client, config, fileBytes, fileName);
String xml = downloadXml(client, config, dataPackageId);
return new LegalRequirementsUploadResult(dataPackageId, xml);
}
public void resetSession(CookieManager cookieManager) {
EventHubProperties.LegalRequirements config = properties.getTachographFileSession().getLegalRequirements();
resetSessionQuietly(httpClient(config, cookieManager), config);
}
public CookieManager newCookieManager() {
return new CookieManager(null, CookiePolicy.ACCEPT_ALL);
}
private String uploadDataPackage(HttpClient client, EventHubProperties.LegalRequirements config, byte[] fileBytes, String fileName) {
try {
String payload = objectMapper.createObjectNode()
@ -115,6 +130,13 @@ public class LegalRequirementsClient {
return HttpRequest.newBuilder(URI.create(url)).timeout(timeout);
}
private HttpClient httpClient(EventHubProperties.LegalRequirements config, CookieManager cookieManager) {
return HttpClient.newBuilder()
.connectTimeout(config.getConnectTimeout())
.cookieHandler(cookieManager == null ? newCookieManager() : cookieManager)
.build();
}
private String basicAuth(EventHubProperties.LegalRequirements config) {
String user = config.getUsername() == null ? "" : config.getUsername();
String password = config.getPassword() == null ? "" : config.getPassword();

View File

@ -56,10 +56,20 @@ public class TachographCompositeSessionService {
}
public CreateTachographCompositeSessionResponse createCompositeSession(CreateTachographCompositeSessionRequest request) {
if (request == null || request.sessionIds() == null || request.sessionIds().isEmpty()) {
return new CreateTachographCompositeSessionResponse(
upsertCompositeSession(null, request == null ? null : request.sessionIds(), request == null ? null : request.label())
);
}
public TachographCompositeSessionSummaryDto upsertCompositeSession(
UUID compositeSessionId,
List<UUID> requestedSessionIds,
String label
) {
if (requestedSessionIds == null || requestedSessionIds.isEmpty()) {
throw new IllegalArgumentException("sessionIds must not be empty.");
}
List<UUID> memberSessionIds = request.sessionIds().stream()
List<UUID> memberSessionIds = requestedSessionIds.stream()
.filter(Objects::nonNull)
.distinct()
.toList();
@ -72,14 +82,20 @@ public class TachographCompositeSessionService {
.filter(value -> value != null && !value.isBlank())
.findFirst()
.orElse("default");
UUID id = compositeSessionId == null ? UUID.randomUUID() : compositeSessionId;
Instant createdAt = compositeSessionId == null
? Instant.now()
: compositeRepository.find(compositeSessionId)
.map(TachographCompositeSession::createdAt)
.orElseGet(Instant::now);
TachographCompositeSession compositeSession = compositeRepository.save(new TachographCompositeSession(
UUID.randomUUID(),
id,
tenantKey,
blankToNull(request.label()),
blankToNull(label),
memberSessionIds,
Instant.now()
createdAt
));
return new CreateTachographCompositeSessionResponse(toSummary(compositeSession, sessions));
return toSummary(compositeSession, sessions);
}
public TachographCompositeSessionSummaryDto getCompositeSession(UUID compositeSessionId) {

View File

@ -2,14 +2,18 @@ package at.procon.eventhub.tachographfilesession.service;
import at.procon.eventhub.config.EventHubProperties;
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
import at.procon.eventhub.tachographfilesession.dto.TachographCompositeSessionSummaryDto;
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverDetailDto;
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryDto;
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteResponse;
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse;
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowResponse;
import at.procon.eventhub.tachographfilesession.model.DriverExtractionSession;
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
import jakarta.servlet.http.HttpSession;
import java.net.CookieManager;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@ -27,9 +31,14 @@ import org.springframework.web.multipart.MultipartFile;
public class TachographFileSessionService {
private static final Logger log = LoggerFactory.getLogger(TachographFileSessionService.class);
private static final String LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE =
TachographFileSessionService.class.getName() + ".legalRequirementsCookieManager";
private static final String WORKFLOW_STATE_ATTRIBUTE =
TachographFileSessionService.class.getName() + ".uploadWorkflowState";
private final EventHubProperties properties;
private final TachographFileSessionRepository repository;
private final TachographCompositeSessionService compositeSessionService;
private final LegalRequirementsClient legalRequirementsClient;
private final TachographXmlParser tachographXmlParser;
private final DriverCardXmlExtractionService driverCardExtractionService;
@ -38,6 +47,7 @@ public class TachographFileSessionService {
public TachographFileSessionService(
EventHubProperties properties,
TachographFileSessionRepository repository,
TachographCompositeSessionService compositeSessionService,
LegalRequirementsClient legalRequirementsClient,
TachographXmlParser tachographXmlParser,
DriverCardXmlExtractionService driverCardExtractionService,
@ -45,6 +55,7 @@ public class TachographFileSessionService {
) {
this.properties = properties;
this.repository = repository;
this.compositeSessionService = compositeSessionService;
this.legalRequirementsClient = legalRequirementsClient;
this.tachographXmlParser = tachographXmlParser;
this.driverCardExtractionService = driverCardExtractionService;
@ -55,14 +66,21 @@ public class TachographFileSessionService {
MultipartFile file,
String tenantKey,
String sourceInstanceKey,
String sessionLabel
String sessionLabel,
HttpSession webSession
) {
try {
if (webSession == null) {
throw new IllegalArgumentException("HTTP session is required for tachograph upload workflow.");
}
validateFile(file);
byte[] fileBytes = file.getBytes();
validateFileBytes(fileBytes);
CookieManager cookieManager = cookieManager(webSession);
TachographUploadWorkflowState workflowState = workflowState(webSession);
long uploadStartedAt = System.nanoTime();
LegalRequirementsUploadResult uploadResult = legalRequirementsClient.uploadTachographFile(fileBytes, file.getOriginalFilename());
LegalRequirementsUploadResult uploadResult =
legalRequirementsClient.uploadTachographFile(cookieManager, fileBytes, file.getOriginalFilename());
long uploadDurationMs = elapsedMillis(uploadStartedAt);
long parseStartedAt = System.nanoTime();
@ -72,6 +90,7 @@ public class TachographFileSessionService {
Instant createdAt = Instant.now();
Instant expiresAt = createdAt.plus(properties.getTachographFileSession().getTtl());
boolean driverCardFile = "DriverCard".equals(parsedXml.rootElementName());
validateWorkflowSequence(webSession, workflowState, cookieManager, driverCardFile);
TachographFileSessionMetadata metadata = new TachographFileSessionMetadata(
tenant(tenantKey),
sourceInstance(sourceInstanceKey, driverCardFile),
@ -100,7 +119,10 @@ public class TachographFileSessionService {
fileBytes.length
);
TachographFileSession saved = repository.save(session);
return new CreateTachographFileSessionResponse(toSummary(saved));
workflowState.recordUploadedSession(saved.sessionId(), driverCardFile);
TachographCompositeSessionSummaryDto compositeSession = updateCompositeSession(workflowState, sessionLabel);
webSession.setAttribute(WORKFLOW_STATE_ATTRIBUTE, workflowState);
return new CreateTachographFileSessionResponse(toSummary(saved), workflowState.toDto(), compositeSession);
} catch (Exception e) {
if (e instanceof RuntimeException runtimeException) {
throw runtimeException;
@ -109,6 +131,27 @@ public class TachographFileSessionService {
}
}
public TachographUploadWorkflowResponse resetUploadWorkflow(HttpSession webSession) {
if (webSession == null) {
throw new IllegalArgumentException("HTTP session is required for tachograph upload workflow.");
}
CookieManager existingCookieManager = existingCookieManager(webSession);
if (existingCookieManager != null) {
legalRequirementsClient.resetSession(existingCookieManager);
}
webSession.setAttribute(LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE, legalRequirementsClient.newCookieManager());
TachographUploadWorkflowState state = new TachographUploadWorkflowState();
webSession.setAttribute(WORKFLOW_STATE_ATTRIBUTE, state);
return new TachographUploadWorkflowResponse(state.toDto());
}
public TachographUploadWorkflowResponse getUploadWorkflow(HttpSession webSession) {
if (webSession == null) {
throw new IllegalArgumentException("HTTP session is required for tachograph upload workflow.");
}
return new TachographUploadWorkflowResponse(workflowState(webSession).toDto());
}
public TachographFileSessionSummaryDto getSession(UUID sessionId) {
return toSummary(requireSession(sessionId));
}
@ -146,6 +189,27 @@ public class TachographFileSessionService {
return repository.find(sessionId).orElseThrow(() -> new TachographFileSessionNotFoundException(sessionId));
}
private TachographCompositeSessionSummaryDto updateCompositeSession(
TachographUploadWorkflowState workflowState,
String sessionLabel
) {
if (workflowState.uploadedSessionIds().size() < 2) {
return null;
}
TachographCompositeSessionSummaryDto compositeSession = compositeSessionService.upsertCompositeSession(
workflowState.compositeSessionId(),
workflowState.uploadedSessionIds(),
compositeSessionLabel(sessionLabel)
);
workflowState.compositeSessionId(compositeSession.compositeSessionId());
return compositeSession;
}
private String compositeSessionLabel(String sessionLabel) {
String normalizedLabel = blankToNull(sessionLabel);
return normalizedLabel == null ? "tachograph-upload-workflow" : normalizedLabel;
}
private TachographFileSessionSummaryDto toSummary(TachographFileSession session) {
return new TachographFileSessionSummaryDto(
session.sessionId(),
@ -207,6 +271,63 @@ public class TachographFileSessionService {
return driverCardFile ? "legalrequirements-drivercard" : "legalrequirements-vehicleunit";
}
private void validateWorkflowSequence(
HttpSession webSession,
TachographUploadWorkflowState workflowState,
CookieManager cookieManager,
boolean driverCardFile
) {
try {
validateWorkflowSequenceInternal(workflowState, driverCardFile);
} catch (IllegalArgumentException exception) {
resetWorkflowState(webSession, cookieManager);
throw exception;
}
}
private void validateWorkflowSequenceInternal(TachographUploadWorkflowState workflowState, boolean driverCardFile) {
if (!workflowState.driverCardUploaded() && !workflowState.uploadedSessionIds().isEmpty() && driverCardFile) {
throw new IllegalArgumentException("Workflow already contains uploaded files. Reset the workflow before starting a new driver card import.");
}
if (!workflowState.driverCardUploaded() && !driverCardFile) {
throw new IllegalArgumentException("Driver card file must be uploaded first after workflow reset.");
}
if (workflowState.driverCardUploaded() && driverCardFile) {
throw new IllegalArgumentException("Workflow already contains a driver card file. Upload vehicle-unit files next or reset the workflow.");
}
}
private CookieManager cookieManager(HttpSession webSession) {
CookieManager existing = existingCookieManager(webSession);
if (existing != null) {
return existing;
}
CookieManager created = legalRequirementsClient.newCookieManager();
webSession.setAttribute(LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE, created);
return created;
}
private CookieManager existingCookieManager(HttpSession webSession) {
Object value = webSession.getAttribute(LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE);
return value instanceof CookieManager cookieManager ? cookieManager : null;
}
private TachographUploadWorkflowState workflowState(HttpSession webSession) {
Object value = webSession.getAttribute(WORKFLOW_STATE_ATTRIBUTE);
if (value instanceof TachographUploadWorkflowState state) {
return state;
}
TachographUploadWorkflowState state = new TachographUploadWorkflowState();
webSession.setAttribute(WORKFLOW_STATE_ATTRIBUTE, state);
return state;
}
private void resetWorkflowState(HttpSession webSession, CookieManager cookieManager) {
legalRequirementsClient.resetSession(cookieManager);
webSession.setAttribute(LEGAL_REQUIREMENTS_COOKIE_MANAGER_ATTRIBUTE, legalRequirementsClient.newCookieManager());
webSession.setAttribute(WORKFLOW_STATE_ATTRIBUTE, new TachographUploadWorkflowState());
}
private String blankToNull(String value) {
return value == null || value.isBlank() ? null : value.trim();
}

View File

@ -0,0 +1,57 @@
package at.procon.eventhub.tachographfilesession.service;
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowDto;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
final class TachographUploadWorkflowState {
private UUID driverCardSessionId;
private final List<UUID> uploadedSessionIds = new ArrayList<>();
private final List<UUID> vehicleUnitSessionIds = new ArrayList<>();
private UUID compositeSessionId;
boolean driverCardUploaded() {
return driverCardSessionId != null;
}
UUID driverCardSessionId() {
return driverCardSessionId;
}
List<UUID> uploadedSessionIds() {
return List.copyOf(uploadedSessionIds);
}
List<UUID> vehicleUnitSessionIds() {
return List.copyOf(vehicleUnitSessionIds);
}
UUID compositeSessionId() {
return compositeSessionId;
}
void recordUploadedSession(UUID sessionId, boolean driverCardFile) {
uploadedSessionIds.add(sessionId);
if (driverCardFile) {
driverCardSessionId = sessionId;
return;
}
vehicleUnitSessionIds.add(sessionId);
}
void compositeSessionId(UUID compositeSessionId) {
this.compositeSessionId = compositeSessionId;
}
TachographUploadWorkflowDto toDto() {
return new TachographUploadWorkflowDto(
driverCardUploaded(),
driverCardSessionId,
uploadedSessionIds(),
vehicleUnitSessionIds(),
compositeSessionId
);
}
}

View File

@ -4,6 +4,10 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HexFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@ -21,6 +25,9 @@ import org.xml.sax.SAXException;
public class TachographXmlParser {
private static final String JAXP_MAX_OCCUR_LIMIT = "http://www.oracle.com/xml/jaxp/properties/maxOccurLimit";
private static final Pattern MANUFACTURER_SPECIFIC_ERROR_CODE_PATTERN = Pattern.compile(
"(<manufacturerSpecificErrorCode>)([^<\\s]+)(</manufacturerSpecificErrorCode>)"
);
private final Schema schema;
@ -62,11 +69,45 @@ public class TachographXmlParser {
}
}
private String normalizeXmlContent(String xmlContent) {
String normalizeXmlContent(String xmlContent) {
if (xmlContent == null || xmlContent.isEmpty()) {
return xmlContent;
}
return xmlContent.charAt(0) == '\uFEFF' ? xmlContent.substring(1) : xmlContent;
String normalized = xmlContent.charAt(0) == '\uFEFF' ? xmlContent.substring(1) : xmlContent;
return normalizeManufacturerSpecificErrorCodes(normalized);
}
private String normalizeManufacturerSpecificErrorCodes(String xmlContent) {
Matcher matcher = MANUFACTURER_SPECIFIC_ERROR_CODE_PATTERN.matcher(xmlContent);
StringBuffer normalized = new StringBuffer(xmlContent.length());
while (matcher.find()) {
String rawValue = matcher.group(2);
String replacementValue = normalizeThreeByteOctetString(rawValue);
matcher.appendReplacement(
normalized,
Matcher.quoteReplacement(matcher.group(1) + replacementValue + matcher.group(3))
);
}
matcher.appendTail(normalized);
return normalized.toString();
}
private String normalizeThreeByteOctetString(String rawValue) {
if (rawValue == null || rawValue.isBlank()) {
return rawValue;
}
if (rawValue.matches("(?i)[0-9a-f]{6}")) {
return rawValue.toUpperCase();
}
try {
byte[] decoded = Base64.getDecoder().decode(rawValue);
if (decoded.length == 3) {
return HexFormat.of().formatHex(decoded).toUpperCase();
}
return rawValue;
} catch (IllegalArgumentException ignored) {
return rawValue;
}
}
private Schema loadSchema() {

View File

@ -226,39 +226,34 @@ public class VehicleUnitXmlExtractionService {
VehicleContext vehicleContext,
List<ExtractionWarning> warnings
) {
NodeList dayRecords = nodes(document, "/VehicleUnit/Activities/vuActivityDailyData");
if (dayRecords.getLength() == 0) {
NodeList activitySections = nodes(document, "/VehicleUnit/Activities");
if (activitySections.getLength() == 0) {
return List.of();
}
LocalDate startDate = vehicleContext.defaultActivityStartDate();
if (startDate == null) {
warnings.add(new ExtractionWarning(
"VU_ACTIVITY_DATE_INFERENCE_FAILED",
"Vehicle-unit activity daily data is present but no base date could be inferred from the VU downloadable period.",
"/VehicleUnit/Activities/vuActivityDailyData"
));
return List.of();
}
LocalDate maxDate = vehicleContext.defaultActivityEndDate();
if (maxDate != null) {
LocalDate inferredEndDate = startDate.plusDays(dayRecords.getLength() - 1L);
if (inferredEndDate.isAfter(maxDate)) {
warnings.add(new ExtractionWarning(
"VU_ACTIVITY_DATE_INFERENCE_RANGE",
"Vehicle-unit activity daily records exceed the VU downloadable-period range when mapped by sequence order.",
"/VehicleUnit/Activities/vuActivityDailyData"
));
}
}
List<ExtractedCardActivityInterval> intervals = new ArrayList<>();
int intervalNo = 0;
int fallbackDayIndex = 0;
for (int activityIndex = 0; activityIndex < activitySections.getLength(); activityIndex++) {
Element activities = (Element) activitySections.item(activityIndex);
NodeList dayRecords = nodes(activities, "vuActivityDailyData");
for (int dayIndex = 0; dayIndex < dayRecords.getLength(); dayIndex++) {
Element dayRecord = (Element) dayRecords.item(dayIndex);
LocalDate date = startDate.plusDays(dayIndex);
String dayPath = "/VehicleUnit/Activities[" + (dayIndex + 1) + "]/vuActivityDailyData";
String dayPath = dayRecords.getLength() == 1
? "/VehicleUnit/Activities[" + (activityIndex + 1) + "]/vuActivityDailyData"
: "/VehicleUnit/Activities[" + (activityIndex + 1) + "]/vuActivityDailyData[" + (dayIndex + 1) + "]";
LocalDate date = resolveActivityDate(
activities,
vehicleContext,
activityIndex,
fallbackDayIndex + dayIndex,
dayPath,
warnings
);
if (date == null) {
continue;
}
NodeList changes = nodes(dayRecord, "activityChangeInfos");
List<ActivityChange> parsedChanges = new ArrayList<>();
for (int changeIndex = 0; changeIndex < changes.getLength(); changeIndex++) {
@ -305,11 +300,48 @@ public class VehicleUnitXmlExtractionService {
));
}
}
fallbackDayIndex += dayRecords.getLength();
}
intervals.sort(Comparator.comparing(ExtractedCardActivityInterval::from));
return intervals;
}
private LocalDate resolveActivityDate(
Element activities,
VehicleContext vehicleContext,
int activityIndex,
int fallbackDayIndex,
String dayPath,
List<ExtractionWarning> warnings
) {
OffsetDateTime downloadTime = offsetDateTime(text(activities, "downloadTime"));
if (downloadTime != null) {
return downloadTime.withOffsetSameInstant(ZoneOffset.UTC).toLocalDate();
}
LocalDate fallbackDate = vehicleContext.defaultActivityStartDate();
if (fallbackDate == null) {
warnings.add(new ExtractionWarning(
"VU_ACTIVITY_DATE_INFERENCE_FAILED",
"Vehicle-unit activity daily data is present but neither Activities/downloadTime nor the VU downloadable period can provide its date.",
dayPath
));
return null;
}
LocalDate resolvedDate = fallbackDate.plusDays(fallbackDayIndex);
LocalDate maxDate = vehicleContext.defaultActivityEndDate();
if (maxDate != null && resolvedDate.isAfter(maxDate)) {
warnings.add(new ExtractionWarning(
"VU_ACTIVITY_DATE_INFERENCE_RANGE",
"Vehicle-unit activity daily records exceed the VU downloadable-period range when mapped by sequence order.",
"/VehicleUnit/Activities[" + (activityIndex + 1) + "]"
));
}
return resolvedDate;
}
private void assignActivityCoverage(
List<ExtractedCardActivityInterval> vuActivityIntervals,
List<VuCardIwInterval> vuCardIwIntervals,

View File

@ -18,6 +18,8 @@ import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryD
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionDeleteResponse;
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionListDriversResponse;
import at.procon.eventhub.tachographfilesession.dto.TachographFileSessionSummaryDto;
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowDto;
import at.procon.eventhub.tachographfilesession.dto.TachographUploadWorkflowResponse;
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
import at.procon.eventhub.tachographfilesession.model.TachographEsperActivityIntervalEvent;
import at.procon.eventhub.tachographfilesession.model.TachographEsperDailyWeeklyRestCandidateCoverageIntervalEvent;
@ -34,6 +36,7 @@ import java.time.Instant;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
@ -49,6 +52,7 @@ class TachographFileSessionControllerTest {
.setControllerAdvice(new TachographFileSessionExceptionHandler())
.build();
UUID sessionId = UUID.randomUUID();
MockHttpSession webSession = new MockHttpSession();
TachographFileDriverSummaryDto driver = new TachographFileDriverSummaryDto("12:123", "Muster", "Max", "12", "123", 3, 2);
TachographFileSessionSummaryDto summary = new TachographFileSessionSummaryDto(
sessionId,
@ -64,8 +68,26 @@ class TachographFileSessionControllerTest {
Instant.parse("2026-05-12T10:00:00Z"),
Instant.parse("2026-05-12T14:00:00Z")
);
when(service.createSession(org.mockito.ArgumentMatchers.any(), eq("default"), eq("src"), eq("sample")))
.thenReturn(new CreateTachographFileSessionResponse(summary));
when(service.createSession(
org.mockito.ArgumentMatchers.any(),
eq("default"),
eq("src"),
eq("sample"),
org.mockito.ArgumentMatchers.any(jakarta.servlet.http.HttpSession.class)
))
.thenReturn(new CreateTachographFileSessionResponse(
summary,
new TachographUploadWorkflowDto(true, sessionId, List.of(sessionId), List.of(), null),
null
));
when(service.resetUploadWorkflow(org.mockito.ArgumentMatchers.any(jakarta.servlet.http.HttpSession.class)))
.thenReturn(new TachographUploadWorkflowResponse(
new TachographUploadWorkflowDto(false, null, List.of(), List.of(), null)
));
when(service.getUploadWorkflow(org.mockito.ArgumentMatchers.any(jakarta.servlet.http.HttpSession.class)))
.thenReturn(new TachographUploadWorkflowResponse(
new TachographUploadWorkflowDto(true, sessionId, List.of(sessionId), List.of(), null)
));
when(service.getSession(sessionId)).thenReturn(summary);
when(service.listDrivers(sessionId)).thenReturn(new TachographFileSessionListDriversResponse(sessionId, List.of(driver)));
when(service.getDriver(sessionId, "12:123")).thenReturn(new TachographFileDriverDetailDto(sessionId, "12:123", null, null, List.of(), List.of(), List.of(), List.of(), List.of(), List.of()));
@ -319,13 +341,23 @@ class TachographFileSessionControllerTest {
));
when(service.deleteSession(sessionId)).thenReturn(new TachographFileSessionDeleteResponse(sessionId, true));
mockMvc.perform(post("/api/eventhub/tachograph-file-sessions/workflow/reset").session(webSession))
.andExpect(status().isOk())
.andExpect(jsonPath("$.workflow.driverCardUploaded").value(false));
mockMvc.perform(multipart("/api/eventhub/tachograph-file-sessions")
.file(new MockMultipartFile("file", "sample.ddd", "application/octet-stream", "abc".getBytes()))
.param("tenantKey", "default")
.param("sourceInstanceKey", "src")
.param("sessionLabel", "sample"))
.param("sessionLabel", "sample")
.session(webSession))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.session.sessionId").value(sessionId.toString()));
.andExpect(jsonPath("$.session.sessionId").value(sessionId.toString()))
.andExpect(jsonPath("$.workflow.driverCardUploaded").value(true));
mockMvc.perform(get("/api/eventhub/tachograph-file-sessions/workflow").session(webSession))
.andExpect(status().isOk())
.andExpect(jsonPath("$.workflow.driverCardSessionId").value(sessionId.toString()));
mockMvc.perform(get("/api/eventhub/tachograph-file-sessions/{sessionId}", sessionId))
.andExpect(status().isOk())

View File

@ -3,22 +3,27 @@ package at.procon.eventhub.tachographfilesession.service;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import at.procon.eventhub.config.EventHubProperties;
import at.procon.eventhub.tachographfilesession.dto.CreateTachographFileSessionResponse;
import at.procon.eventhub.tachographfilesession.dto.TachographCompositeSessionSummaryDto;
import at.procon.eventhub.tachographfilesession.dto.TachographFileDriverSummaryDto;
import at.procon.eventhub.tachographfilesession.model.ExtractionStats;
import at.procon.eventhub.tachographfilesession.model.TachographFileSession;
import at.procon.eventhub.tachographfilesession.model.TachographFileSessionMetadata;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.mock.web.MockHttpSession;
class TachographFileSessionServiceTest {
@ -26,6 +31,7 @@ class TachographFileSessionServiceTest {
void createsAndLoadsSession() {
EventHubProperties properties = new EventHubProperties();
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
TachographCompositeSessionService compositeSessionService = Mockito.mock(TachographCompositeSessionService.class);
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
DriverCardXmlExtractionService driverCardExtractor = Mockito.mock(DriverCardXmlExtractionService.class);
@ -33,14 +39,17 @@ class TachographFileSessionServiceTest {
TachographFileSessionService service = new TachographFileSessionService(
properties,
repository,
compositeSessionService,
client,
parser,
driverCardExtractor,
vehicleUnitExtractor
);
when(client.newCookieManager()).thenReturn(new java.net.CookieManager());
MockMultipartFile file = new MockMultipartFile("file", "sample.ddd", "application/octet-stream", "abc".getBytes(StandardCharsets.UTF_8));
when(client.uploadTachographFile(any(), eq("sample.ddd"))).thenReturn(new LegalRequirementsUploadResult("42", "<DriverCard/>"));
when(client.uploadTachographFile(any(java.net.CookieManager.class), any(), eq("sample.ddd")))
.thenReturn(new LegalRequirementsUploadResult("42", "<DriverCard/>"));
TachographXmlParser.ParsedTachographXml parsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
when(parsed.rootElementName()).thenReturn("DriverCard");
when(parser.parse("<DriverCard/>")).thenReturn(parsed);
@ -55,9 +64,12 @@ class TachographFileSessionServiceTest {
);
when(driverCardExtractor.extract(eq(parsed), any(), any(), any())).thenReturn(extracted);
CreateTachographFileSessionResponse response = service.createSession(file, null, null, "sample");
CreateTachographFileSessionResponse response = service.createSession(file, null, null, "sample", new MockHttpSession());
assertThat(response.session().sessionId()).isEqualTo(extracted.sessionId());
assertThat(response.workflow()).isNotNull();
assertThat(response.workflow().driverCardUploaded()).isTrue();
assertThat(response.compositeSession()).isNull();
assertThat(service.getSession(extracted.sessionId()).sessionId()).isEqualTo(extracted.sessionId());
}
@ -66,6 +78,7 @@ class TachographFileSessionServiceTest {
EventHubProperties properties = new EventHubProperties();
properties.getTachographFileSession().setMaxFileSizeBytes(1024);
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
TachographCompositeSessionService compositeSessionService = Mockito.mock(TachographCompositeSessionService.class);
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
DriverCardXmlExtractionService driverCardExtractor = Mockito.mock(DriverCardXmlExtractionService.class);
@ -73,6 +86,7 @@ class TachographFileSessionServiceTest {
TachographFileSessionService service = new TachographFileSessionService(
properties,
repository,
compositeSessionService,
client,
parser,
driverCardExtractor,
@ -81,17 +95,18 @@ class TachographFileSessionServiceTest {
MockMultipartFile file = new MockMultipartFile("file", "sample.ddd", "application/octet-stream", new byte[1025]);
assertThatThrownBy(() -> service.createSession(file, null, null, "sample"))
assertThatThrownBy(() -> service.createSession(file, null, null, "sample", new MockHttpSession()))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("size limit");
verifyNoInteractions(client, parser, driverCardExtractor, vehicleUnitExtractor);
verifyNoInteractions(compositeSessionService, client, parser, driverCardExtractor, vehicleUnitExtractor);
}
@Test
void usesVehicleUnitDefaultsAndExtractorForVehicleUnitXml() {
EventHubProperties properties = new EventHubProperties();
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
TachographCompositeSessionService compositeSessionService = Mockito.mock(TachographCompositeSessionService.class);
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
DriverCardXmlExtractionService driverCardExtractor = Mockito.mock(DriverCardXmlExtractionService.class);
@ -99,14 +114,17 @@ class TachographFileSessionServiceTest {
TachographFileSessionService service = new TachographFileSessionService(
properties,
repository,
compositeSessionService,
client,
parser,
driverCardExtractor,
vehicleUnitExtractor
);
when(client.newCookieManager()).thenReturn(new java.net.CookieManager());
MockMultipartFile file = new MockMultipartFile("file", "vu.ddd", "application/octet-stream", "vu".getBytes(StandardCharsets.UTF_8));
when(client.uploadTachographFile(any(), eq("vu.ddd"))).thenReturn(new LegalRequirementsUploadResult("77", "<VehicleUnit/>"));
when(client.uploadTachographFile(any(java.net.CookieManager.class), any(), eq("vu.ddd")))
.thenReturn(new LegalRequirementsUploadResult("77", "<VehicleUnit/>"));
TachographXmlParser.ParsedTachographXml parsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
when(parsed.rootElementName()).thenReturn("VehicleUnit");
when(parser.parse("<VehicleUnit/>")).thenReturn(parsed);
@ -121,9 +139,74 @@ class TachographFileSessionServiceTest {
);
when(vehicleUnitExtractor.extract(eq(parsed), any(), any(), any())).thenReturn(extracted);
CreateTachographFileSessionResponse response = service.createSession(file, null, null, "vu");
MockHttpSession webSession = new MockHttpSession();
service.resetUploadWorkflow(webSession);
MockMultipartFile driverCardFile = new MockMultipartFile("file", "driver.ddd", "application/octet-stream", "dc".getBytes(StandardCharsets.UTF_8));
when(client.uploadTachographFile(any(java.net.CookieManager.class), any(), eq("driver.ddd")))
.thenReturn(new LegalRequirementsUploadResult("42", "<DriverCard/>"));
TachographXmlParser.ParsedTachographXml driverParsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
when(driverParsed.rootElementName()).thenReturn("DriverCard");
when(parser.parse("<DriverCard/>")).thenReturn(driverParsed);
TachographFileSession driverExtracted = new TachographFileSession(
UUID.randomUUID(),
new TachographFileSessionMetadata("default", "legalrequirements-drivercard", "sample", "driver.ddd", "a", 2, "42", "b", true, null),
Map.of(),
new ExtractionStats(0, 0, 0, 0, 0, 0),
java.util.List.of(),
Instant.now(),
Instant.now().plusSeconds(10)
);
when(driverCardExtractor.extract(eq(driverParsed), any(), any(), any())).thenReturn(driverExtracted);
when(compositeSessionService.upsertCompositeSession(any(), anyList(), any()))
.thenReturn(new TachographCompositeSessionSummaryDto(
UUID.randomUUID(),
"default",
"vu",
List.of(driverExtracted.sessionId(), extracted.sessionId()),
List.<TachographFileDriverSummaryDto>of(),
Instant.now()
));
service.createSession(driverCardFile, null, null, "sample", webSession);
CreateTachographFileSessionResponse response = service.createSession(file, null, null, "vu", webSession);
assertThat(response.session().driverCardFile()).isFalse();
assertThat(response.session().sourceInstanceKey()).isEqualTo("legalrequirements-vehicleunit");
assertThat(response.compositeSession()).isNotNull();
assertThat(response.workflow().uploadedSessionIds()).hasSize(2);
}
@Test
void rejectsVehicleUnitBeforeDriverCardInWorkflow() {
EventHubProperties properties = new EventHubProperties();
TachographFileSessionRepository repository = new InMemoryTachographFileSessionRepository(properties);
TachographCompositeSessionService compositeSessionService = Mockito.mock(TachographCompositeSessionService.class);
LegalRequirementsClient client = Mockito.mock(LegalRequirementsClient.class);
TachographXmlParser parser = Mockito.mock(TachographXmlParser.class);
TachographFileSessionService service = new TachographFileSessionService(
properties,
repository,
compositeSessionService,
client,
parser,
Mockito.mock(DriverCardXmlExtractionService.class),
Mockito.mock(VehicleUnitXmlExtractionService.class)
);
when(client.newCookieManager()).thenReturn(new java.net.CookieManager());
when(client.uploadTachographFile(any(java.net.CookieManager.class), any(), eq("vu.ddd")))
.thenReturn(new LegalRequirementsUploadResult("77", "<VehicleUnit/>"));
TachographXmlParser.ParsedTachographXml parsed = Mockito.mock(TachographXmlParser.ParsedTachographXml.class);
when(parsed.rootElementName()).thenReturn("VehicleUnit");
when(parser.parse("<VehicleUnit/>")).thenReturn(parsed);
assertThatThrownBy(() -> service.createSession(
new MockMultipartFile("file", "vu.ddd", "application/octet-stream", "vu".getBytes(StandardCharsets.UTF_8)),
null,
null,
"vu",
new MockHttpSession()
))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Driver card file must be uploaded first");
}
}

View File

@ -43,6 +43,26 @@ class TachographXmlParserTest {
.isInstanceOf(TachographXmlValidationException.class);
}
@Test
void normalizesManufacturerSpecificErrorCodeFromBase64ToHex() {
String normalized = parser.normalizeXmlContent("""
<VehicleUnit>
<EventsFaults>
<vuFaultData>
<vuFaultRecords>
<manufacturerSpecificEventFaultData>
<manufacturerCode>0</manufacturerCode>
<manufacturerSpecificErrorCode>////</manufacturerSpecificErrorCode>
</manufacturerSpecificEventFaultData>
</vuFaultRecords>
</vuFaultData>
</EventsFaults>
</VehicleUnit>
""");
assertThat(normalized).contains("<manufacturerSpecificErrorCode>FFFFFF</manufacturerSpecificErrorCode>");
}
@Test
void rejectsXmlWithDoctype() {
String xmlWithDoctype = """

View File

@ -111,6 +111,35 @@ class VehicleUnitXmlExtractionServiceTest {
assertThat(secondDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z");
}
@Test
void usesActivitiesDownloadTimeAsVuActivityRecordDate() throws Exception {
TachographFileSession session = service.extract(
new TachographXmlParser.ParsedTachographXml(document(VehicleUnitXmlSamples.vehicleUnitXmlWithActivityDownloadTime()), "VehicleUnit"),
new TachographFileSessionMetadata(
"default",
"legalrequirements-vehicleunit",
"sample-vu",
"sample-vu.ddd",
"abc",
10,
"42",
"def",
false,
null
),
Instant.now(),
Instant.now().plus(4, ChronoUnit.HOURS)
);
DriverExtractionSession driver = session.driversByKey().get("12:12345678901200");
assertThat(driver).isNotNull();
assertThat(driver.cardActivityIntervals()).hasSize(2);
assertThat(driver.cardActivityIntervals().get(0).from()).isEqualTo(OffsetDateTime.parse("2026-05-25T08:00:00Z"));
assertThat(driver.cardActivityIntervals().get(1).from()).isEqualTo(OffsetDateTime.parse("2026-05-25T09:00:00Z"));
assertThat(driver.cardActivityIntervals().get(1).to()).isEqualTo(OffsetDateTime.parse("2026-05-26T00:00:00Z"));
assertThat(session.warnings()).isEmpty();
}
@Test
void keepsOpenVuCardUsageRecordWithoutClosingItAtDownloadPeriodEnd() throws Exception {
TachographFileSession session = service.extract(

View File

@ -254,4 +254,64 @@ final class VehicleUnitXmlSamples {
.replace("<timeOfChange>11:00:00Z</timeOfChange>", "<timeOfChange>11:00:00</timeOfChange>")
.replace("<timeOfChange>07:30:00Z</timeOfChange>", "<timeOfChange>07:30:00</timeOfChange>");
}
static String vehicleUnitXmlWithActivityDownloadTime() {
return """
<VehicleUnit>
<Overview>
<vehicleIdentificationNumber>VINVU123456789012</vehicleIdentificationNumber>
<vehicleRegistrationIdentification>
<vehicleRegistrationNation>12</vehicleRegistrationNation>
<vehicleRegistrationNumber><vehicleRegNumber>W-1000V</vehicleRegNumber></vehicleRegistrationNumber>
</vehicleRegistrationIdentification>
<vuDownloadablePeriod>
<minDownloadableTime>2026-05-20T00:00:00Z</minDownloadableTime>
<maxDownloadableTime>2026-05-20T23:59:59Z</maxDownloadableTime>
</vuDownloadablePeriod>
</Overview>
<Activities Generation="3">
<downloadTime>2026-05-25T23:59:59Z</downloadTime>
<odometerValueMidnight>3756</odometerValueMidnight>
<vuCardIWData>
<vuCardIWRecords>
<cardHolderName>
<holderSurname><name>Muster</name></holderSurname>
<holderFirstNames><name>Max</name></holderFirstNames>
</cardHolderName>
<fullCardNumber>
<cardType>DRIVER_CARD</cardType>
<cardIssuingMemberState>12</cardIssuingMemberState>
<cardNumber>
<driverIdentification>123456789012</driverIdentification>
<cardReplacementIndex>0</cardReplacementIndex>
<cardRenewalIndex>0</cardRenewalIndex>
</cardNumber>
<generation>3</generation>
</fullCardNumber>
<cardInsertionTime>2026-05-25T08:00:00Z</cardInsertionTime>
<vehicleOdometerValueAtInsertion>3700</vehicleOdometerValueAtInsertion>
<cardSlotNumber>DRIVER</cardSlotNumber>
</vuCardIWRecords>
</vuCardIWData>
<vuActivityDailyData>
<noOfActivityChanges>2</noOfActivityChanges>
<activityChangeInfos>
<slot>DRIVER</slot>
<drivingStatus>SINGLE</drivingStatus>
<cardStatus>INSERTED</cardStatus>
<activity>WORK</activity>
<timeOfChange>08:00:00Z</timeOfChange>
</activityChangeInfos>
<activityChangeInfos>
<slot>DRIVER</slot>
<drivingStatus>SINGLE</drivingStatus>
<cardStatus>INSERTED</cardStatus>
<activity>DRIVING</activity>
<timeOfChange>09:00:00Z</timeOfChange>
</activityChangeInfos>
</vuActivityDailyData>
</Activities>
</VehicleUnit>
""";
}
}