Skip to content

Commit 4094f9a

Browse files
Code review verbeteringen en documentatie updates (#24)
* Code review verbeteringen en documentatie updates - SQL injection preventie: table name validatie in ClickHouseRepository - Connection leak fix: shutdown() sluit ClickHouse client via repository.close() - Veilige cast naar ReadableSpan in LogboekInterceptor (as? i.p.v. as) - Lazy initialisatie van serviceName in ProcessingHandler - Provided scope voor jakarta.ws.rs-api en microprofile-config-api - All-open plugin uitgebreid met @interceptor annotatie - ClickHouse Docker image gepind naar versie 25 - README hernoemd naar JVM, Kotlin voorbeeld toegevoegd - Workflow hernoemd naar "CI with Maven" - Typo fix in ClickHouseSpanExporterTest Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Code review bevindingen verwerken - shutdown() vangt nu exceptions op en retourneert ofFailure() - Test verificatie toegevoegd voor repository.close() in shutdown - Parameterized tests voor table name validatie - ClickHouse image gepind naar versie 25.3 (LTS) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Tabelnaam als veld van ClickHouseRepository valideren bij initialisatie De tabelnaam werd bij elke insert en ensureSchema opnieuw uit de config gelezen en gevalideerd. Nu wordt de tabelnaam eenmalig bij constructie opgeslagen en gevalideerd. De table parameter is verwijderd uit insertJsonEachRow en de tableName is verwijderd uit ClickHouseSpanExporter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 08a2390 commit 4094f9a

File tree

10 files changed

+172
-63
lines changed

10 files changed

+172
-63
lines changed

.github/workflows/maven.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# separate terms of service, privacy policy, and support
77
# documentation.
88

9-
name: Java CI with Maven
9+
name: CI with Maven
1010

1111
permissions: read-all
1212

readme.md renamed to README.md

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# Logboek Dataverwerkingen Java implementatie
1+
# Logboek Dataverwerkingen JVM implementatie
22

33
![Project Pre-Alpha Status](https://img.shields.io/badge/life_cycle-pre_alpha-red)
44
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/MinBZK/moza-logboekdataverwerking/badge)](https://scorecard.dev/viewer/?uri=github.com/MinBZK/moza-logboekdataverwerking)
55

6-
Dit is een Kotlin implementatie van de - in ontwikkeling zijnde - standaard Logboek Dataverwerkingen (LDV) van Logius.
6+
Dit is een Kotlin implementatie van de - in ontwikkeling zijnde - standaard Logboek Dataverwerkingen (LDV) van Logius. De library is bruikbaar vanuit zowel Kotlin als Java projecten.
77

88
## Inleiding
99

@@ -22,7 +22,8 @@ Dit Open Source project is opgezet om de LDV standaard eenvoudig aan nieuwe of b
2222

2323
Om deze package te gebruiken moet je in je (maven) project de volgende variablen in je `application.properties` file toevoegen:
2424

25-
```
25+
```properties
26+
logboekdataverwerking.enabled=true
2627
logboekdataverwerking.service-name=service-name
2728
logboekdataverwerking.clickhouse.endpoint=http://localhost:8123
2829
logboekdataverwerking.clickhouse.username=user
@@ -33,8 +34,9 @@ logboekdataverwerking.clickhouse.table=table_name
3334

3435
of `application.yml`:
3536

36-
```
37+
```yaml
3738
logboekdataverwerking:
39+
enabled: true
3840
service-name: service-name
3941
clickhouse:
4042
endpoint: http://localhost:8123
@@ -52,35 +54,63 @@ Hierbij is `name` de beschrijving van je eigen trace log en `processingActivityI
5254

5355
Daarnaast kan er in de betreffende functie extra informatie aan de Span worden toegevoegd:
5456

55-
```java
56-
57-
@Inject
58-
ProcessingHandler handler;
59-
60-
@Inject
61-
LogboekContext logboekContext;
62-
63-
@GET
64-
@Path("/{identificatieType}/{identificatieNummer}")
65-
@Logboek(name = "test", processingActivityId = "1")
66-
public Response test() throws InterruptedException {
67-
var innerSpan = handler.startSpan("span-2", null);
68-
Thread.sleep(1000);
69-
LogboekContext innerContext = new LogboekContext();
70-
innerContext.setStatus(StatusCode.ERROR);
71-
innerContext.setDataSubjectId("123");
72-
innerContext.setDataSubjectType("BSN");
73-
innerContext.setProcessingActivityId("4321");
74-
innerContext.addLogboekContextToSpan(innerSpan);
75-
innerSpan.end();
57+
**Kotlin:**
58+
```kotlin
59+
@Inject
60+
lateinit var handler: ProcessingHandler
61+
62+
@Inject
63+
lateinit var logboekContext: LogboekContext
64+
65+
@GET
66+
@Path("/{identificatieType}/{identificatieNummer}")
67+
@Logboek(name = "test", processingActivityId = "1")
68+
fun test(): Response {
69+
val innerSpan = handler.startSpan("span-2", null)
70+
val innerContext = LogboekContext().apply {
71+
status = StatusCode.ERROR
72+
dataSubjectId = "123"
73+
dataSubjectType = "BSN"
74+
processingActivityId = "4321"
75+
}
76+
handler.addLogboekContextToSpan(innerSpan, innerContext)
77+
innerSpan.end()
7678
77-
logboekContext.setDataSubjectId("000000000");
78-
logboekContext.setDataSubjectType("KVK");
79-
logboekContext.setStatus(StatusCode.OK);
79+
logboekContext.dataSubjectId = "000000000"
80+
logboekContext.dataSubjectType = "KVK"
81+
logboekContext.status = StatusCode.OK
8082
83+
return Response.ok("Hello world").build()
84+
}
85+
```
8186

82-
return Response.ok("Hello world").build();
83-
}
87+
**Java:**
88+
```java
89+
@Inject
90+
ProcessingHandler handler;
91+
92+
@Inject
93+
LogboekContext logboekContext;
94+
95+
@GET
96+
@Path("/{identificatieType}/{identificatieNummer}")
97+
@Logboek(name = "test", processingActivityId = "1")
98+
public Response test() {
99+
var innerSpan = handler.startSpan("span-2", null);
100+
LogboekContext innerContext = new LogboekContext();
101+
innerContext.setStatus(StatusCode.ERROR);
102+
innerContext.setDataSubjectId("123");
103+
innerContext.setDataSubjectType("BSN");
104+
innerContext.setProcessingActivityId("4321");
105+
handler.addLogboekContextToSpan(innerSpan, innerContext);
106+
innerSpan.end();
107+
108+
logboekContext.setDataSubjectId("000000000");
109+
logboekContext.setDataSubjectType("KVK");
110+
logboekContext.setStatus(StatusCode.OK);
111+
112+
return Response.ok("Hello world").build();
113+
}
84114
```
85115
### Uitschakelen tijdens testen
86116

compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
services:
22
clickhouse:
3-
image: clickhouse/clickhouse-server:latest
3+
image: clickhouse/clickhouse-server:25.3
44
ports:
55
- "8123:8123"
66
environment:

pom.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<version>1.1.0-SNAPSHOT</version>
1010

1111
<name>Logboek Dataverwerkingen Clickhouse</name>
12-
<description>Java utilities to integrate the Logboek Dataverwerkingen (LDV) standard using a Clickhouse DB by Logius.</description>
12+
<description>JVM utilities to integrate the Logboek Dataverwerkingen (LDV) standard using a Clickhouse DB by Logius.</description>
1313
<url>https://github.com/MinBZK/moza-logboekdataverwerking</url>
1414

1515
<licenses>
@@ -120,6 +120,7 @@
120120
<groupId>jakarta.ws.rs</groupId>
121121
<artifactId>jakarta.ws.rs-api</artifactId>
122122
<version>4.0.0</version>
123+
<scope>provided</scope>
123124
</dependency>
124125

125126
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
@@ -135,6 +136,7 @@
135136
<groupId>org.eclipse.microprofile.config</groupId>
136137
<artifactId>microprofile-config-api</artifactId>
137138
<version>3.1</version>
139+
<scope>provided</scope>
138140
</dependency>
139141

140142
<!-- Kotlin -->
@@ -287,6 +289,7 @@
287289
<pluginOptions>
288290
<option>all-open:annotation=jakarta.enterprise.context.ApplicationScoped</option>
289291
<option>all-open:annotation=jakarta.enterprise.context.RequestScoped</option>
292+
<option>all-open:annotation=jakarta.interceptor.Interceptor</option>
290293
</pluginOptions>
291294
</configuration>
292295
<dependencies>

src/main/kotlin/nl/mijnoverheidzakelijk/ldv/exporter/ClickHouseSpanExporter.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper
44
import io.opentelemetry.sdk.common.CompletableResultCode
55
import io.opentelemetry.sdk.trace.data.SpanData
66
import io.opentelemetry.sdk.trace.export.SpanExporter
7-
import nl.mijnoverheidzakelijk.ldv.config.ConfigurationLoader
87
import nl.mijnoverheidzakelijk.ldv.repository.ClickHouseRepository
98
import java.util.concurrent.TimeUnit
109
import java.util.logging.Level
@@ -20,7 +19,6 @@ import java.util.logging.Logger
2019
*/
2120
class ClickHouseSpanExporter (
2221
private val repository: ClickHouseRepository = ClickHouseRepository(),
23-
private val tableName: String = ConfigurationLoader.clickhouseTable,
2422
private val objectMapper: ObjectMapper = ObjectMapper()
2523
) : SpanExporter {
2624

@@ -51,7 +49,7 @@ class ClickHouseSpanExporter (
5149
}
5250

5351
if (payload.isNotEmpty()) {
54-
repository.insertJsonEachRow(tableName, payload.toString())
52+
repository.insertJsonEachRow(payload.toString())
5553
}
5654
} catch (e: Exception) {
5755
LOGGER.log(Level.SEVERE, "Failed to insert spans into ClickHouse", e)
@@ -89,11 +87,19 @@ class ClickHouseSpanExporter (
8987
override fun flush(): CompletableResultCode = CompletableResultCode.ofSuccess()
9088

9189
/**
92-
* Shuts down the exporter. No resources to free, returns success.
93-
*
90+
* Shuts down the exporter and closes the underlying ClickHouse client.
91+
*
9492
* @return success result code
9593
*/
96-
override fun shutdown(): CompletableResultCode = CompletableResultCode.ofSuccess()
94+
override fun shutdown(): CompletableResultCode {
95+
return try {
96+
repository.close()
97+
CompletableResultCode.ofSuccess()
98+
} catch (e: Exception) {
99+
LOGGER.log(Level.SEVERE, "Failed to close ClickHouse client", e)
100+
CompletableResultCode.ofFailure()
101+
}
102+
}
97103

98104
companion object {
99105
private val LOGGER: Logger = Logger.getLogger(ClickHouseSpanExporter::class.java.getName())

src/main/kotlin/nl/mijnoverheidzakelijk/ldv/logboekdataverwerking/LogboekInterceptor.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ class LogboekInterceptor {
6565
span.setStatus(StatusCode.ERROR)
6666
throw e
6767
} finally {
68-
val spanData = (span as ReadableSpan).toSpanData()
69-
7068
if (headers.getHeaderString("traceparent") != null) {
71-
span.setAttribute("dpl.core.foreign_operation.span_id", spanData.parentSpanId)
69+
(span as? ReadableSpan)?.toSpanData()?.let { spanData ->
70+
span.setAttribute("dpl.core.foreign_operation.span_id", spanData.parentSpanId)
71+
}
7272

7373
//todo hoe krijgen we de url, bijv. header. Hier is het team van LDV nog mee bezig.
7474
//todo How do we get the url, ex. header. This is still being worked on by the LDV team.

src/main/kotlin/nl/mijnoverheidzakelijk/ldv/logboekdataverwerking/ProcessingHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class ProcessingHandler {
7070
_openTelemetry = value
7171
}
7272

73-
val serviceName: String = ConfigurationLoader.serviceName
73+
val serviceName: String by lazy { ConfigurationLoader.serviceName }
7474

7575
/**
7676
* Initializes and returns the global [OpenTelemetry] instance.

src/main/kotlin/nl/mijnoverheidzakelijk/ldv/repository/ClickHouseRepository.kt

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,26 @@ import java.util.concurrent.TimeUnit
1313
* Repository encapsulating basic ClickHouse operations used by the exporter.
1414
*/
1515
class ClickHouseRepository {
16+
private val table: String = ConfigurationLoader.clickhouseTable
1617
private val client: Client = Client.Builder()
1718
.addEndpoint(ConfigurationLoader.clickhouseEndpoint)
1819
.setUsername(ConfigurationLoader.clickhouseUsername)
1920
.setPassword(ConfigurationLoader.clickhousePassword)
2021
.setDefaultDatabase(ConfigurationLoader.clickhouseDatabase)
2122
.build()
2223

24+
init {
25+
requireValidTableName(table)
26+
}
27+
2328
/**
2429
* Ensures that the target table exists with the expected schema.
25-
*
30+
*
2631
* @throws ConfigurationException if the table name cannot be resolved
2732
* @throws RuntimeException if the DDL operation fails
2833
*/
2934
@Throws(ConfigurationException::class)
3035
fun ensureSchema() {
31-
val table = ConfigurationLoader.clickhouseTable
3236
try {
3337
// Schema matching SpanData structure (camelCase)
3438
client.query(
@@ -54,18 +58,31 @@ class ClickHouseRepository {
5458
}
5559

5660
/**
57-
* Inserts a JSON payload into the specified table.
61+
* Inserts a JSON payload into the configured table.
5862
*
59-
* @param table the target table name
6063
* @param jsonEachRowPayload payload where each line is a JSON object
6164
* @throws RuntimeException if the insert fails
6265
*/
63-
fun insertJsonEachRow(table: String, jsonEachRowPayload: String) {
66+
fun insertJsonEachRow(jsonEachRowPayload: String) {
6467
try {
6568
val data: InputStream = ByteArrayInputStream(jsonEachRowPayload.toByteArray(StandardCharsets.UTF_8))
6669
client.insert(table, data, ClickHouseFormat.JSONEachRow).get()
6770
} catch (e: Exception) {
6871
throw RuntimeException("Failed to insert into ClickHouse", e)
6972
}
7073
}
74+
75+
fun close() {
76+
client.close()
77+
}
78+
79+
companion object {
80+
private val TABLE_NAME_PATTERN = Regex("^[a-zA-Z_][a-zA-Z0-9_.]*$")
81+
82+
private fun requireValidTableName(table: String) {
83+
require(TABLE_NAME_PATTERN.matches(table)) {
84+
"Invalid table name: $table"
85+
}
86+
}
87+
}
7188
}

0 commit comments

Comments
 (0)