Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
b6afa8c
fix(QTDI-3358): Bump CXF to 4.1.8 and migrate to jakarta.*
undx Sep 22, 2026
6dbe985
chore(QTDI-3358): Record build-validation gotchas in repository-knowl…
undx Sep 22, 2026
5560d23
fix(QTDI-3358): address review round 1 — guard Johnzon-specific Jsonb…
undx Sep 22, 2026
a6224fd
fix(QTDI-3358): address review round 1 — isolate beam-sample-test forks
undx Sep 22, 2026
475b436
chore(QTDI-3358): record round 1 module-specific build/design gotchas
undx Sep 22, 2026
5d0e713
fix(QTDI-3358): address review round 2 — pin jakarta johnzon in 4 poms
undx Sep 22, 2026
8594563
fix(QTDI-3358): address review round 2 — fix idx off-by-one in getVal…
undx Sep 22, 2026
ba099ea
fix(QTDI-3358): address review round 2 — read @CacheRemoveAll's own a…
undx Sep 22, 2026
a403f79
fix(QTDI-3358): address review round 2 — add JakartaJAXRSClientTest
undx Sep 22, 2026
0137b55
docs(QTDI-3358): record component-tools-webapp module knowledge — CXF…
undx Sep 23, 2026
a539722
fix(QTDI-3358): address review round 2 amendment — real Sonar analysi…
undx Sep 23, 2026
cc3c171
fix(QTDI-3358): resolve remaining PR #1283 Sonar issues and raise jca…
undx Sep 23, 2026
32fc335
fix(QTDI-3358): resolve final 4 Sonar new-code issues on PR #1283
undx Sep 23, 2026
27a2220
fix(QTDI-3358): revert incorrectly migrated jakarta imports in docume…
undx Sep 23, 2026
26f95a8
docs(QTDI-3358): record documentation module knowledge — connector sa…
undx Sep 23, 2026
5d156b6
fix(QTDI-3358): unify tomcat/meecrowave/owb to a single jakarta versi…
undx Sep 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,4 @@ documentation/rebuildpdf.bat
.claude/CLAUDE.md
.github/copilot-instructions.md
.claude/settings.json
.ai-commons/skills/manage-zephyr-test-cases/scripts/.env
10 changes: 10 additions & 0 deletions component-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@
<artifactId>johnzon-jsonb</artifactId>
<scope>test</scope>
</dependency>
<!-- Provided-scope only (not transitive to consumers): lets Schema.java dual-annotate a
handful of methods with both javax.json.bind.annotation.JsonbTransient (this module's own,
primary, javax-only line) and jakarta.json.bind.annotation.JsonbTransient, so the exclusion
is also honored by consumers (e.g. component-server) whose own Jsonb runtime is jakarta-based
and would otherwise be unable to recognize the javax-typed annotation at all. See QTDI-3358. -->
<dependency>
<groupId>jakarta.json.bind</groupId>
<artifactId>jakarta.json.bind-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,12 @@ public interface Schema {
/**
* @return All entries, including data and metadata, of this schema.
*/
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
Stream<Entry> getAllEntries();

@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
default Map<String, Entry> getEntryMap() {
throw new UnsupportedOperationException("#getEntryMap is not implemented");
}
Expand All @@ -91,6 +94,7 @@ default Schema.Builder toBuilder() {
* @return all entries ordered
*/
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
default List<Entry> getEntriesOrdered() {
return getEntriesOrdered(naturalOrder());
}
Expand All @@ -103,6 +107,7 @@ default List<Entry> getEntriesOrdered() {
* @return all entries ordered with provided comparator
*/
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
default List<Entry> getEntriesOrdered(final Comparator<Entry> comparator) {
return getAllEntries().sorted(comparator).collect(Collectors.toList());
}
Expand Down Expand Up @@ -211,6 +216,8 @@ interface Entry {
/**
* @return the raw name of this entry if exists, else return name.
*/
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
String getOriginalFieldName();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public String getRawName() {
}

@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
@Override
public String getOriginalFieldName() {
return null;
Expand Down
11 changes: 11 additions & 0 deletions component-runtime-beam/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@
</dependencyManagement>

<dependencies>
<!-- Provided-scope only (not transitive): lets AvroSchema/AvroRecord dual-annotate a handful of
methods/fields with both javax.json.bind.annotation.JsonbTransient (this module's own,
primary, javax-only line) and jakarta.json.bind.annotation.JsonbTransient, mirroring the
same fix in component-api/component-runtime-impl - so a jakarta-based Jsonb runtime (e.g.
component-server) also honors the exclusion when serializing an Avro-backed Schema/Record.
See QTDI-3358. -->
<dependency>
<groupId>jakarta.json.bind</groupId>
<artifactId>jakarta.json.bind-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.talend.sdk.component</groupId>
<artifactId>component-runtime-manager</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ public class AvroRecord implements Record, AvroPropertyMapper, Unwrappable {
private static final RecordConverters RECORD_CONVERTERS = new RecordConverters();

@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
private final IndexedRecord delegate;

@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
private final AvroSchema schema;

public AvroRecord(final IndexedRecord record) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,19 @@ static AvroSchema toAvroSchema(final org.talend.sdk.component.api.record.Schema
}

@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
private final Schema delegate;

private AvroSchema elementSchema;

private List<Entry> entries;

@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
private Map<String, Entry> entryMap;

@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
private List<Entry> metadataEntries;

private Type type;
Expand Down Expand Up @@ -173,12 +176,14 @@ public List<Entry> getMetadata() {

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public Stream<Entry> getAllEntries() {
return Stream.concat(this.getEntries().stream(), this.getMetadata().stream());
}

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public Map<String, Entry> getEntryMap() {
synchronized (this) {
if (entryMap == null || entryMap.isEmpty()) {
Expand All @@ -193,6 +198,7 @@ public Map<String, Entry> getEntryMap() {

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public EntriesOrder naturalOrder() {
return EntriesOrder.of(getActualDelegate().getProp(ENTRIES_ORDER_PROP));
}
Expand Down
11 changes: 11 additions & 0 deletions component-runtime-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
</dependency>
<!-- Provided-scope only (not transitive): lets these classes dual-annotate a handful of
methods with both javax.json.bind.annotation.JsonbTransient (this module's own,
primary, javax-only line) and jakarta.json.bind.annotation.JsonbTransient, so the
exclusion is also honored by consumers (e.g. component-server) whose own Jsonb runtime
is jakarta-based and would otherwise be unable to recognize the javax-typed annotation
at all. See QTDI-3358. -->
<dependency>
<groupId>jakarta.json.bind</groupId>
<artifactId>jakarta.json.bind-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public final class RecordImpl implements Record {

@Getter
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
private final Schema schema;

private RecordImpl(final Map<String, Object> values, final Schema schema) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,19 @@ public class SchemaImpl implements Schema {
private final List<Entry> entries;

@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
private final List<Entry> metadataEntries;

@Getter
private final Map<String, String> props;

@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
private final EntriesOrder entriesOrder;

@Getter
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
private Map<String, Entry> entryMap = new HashMap<>();

public static final String ENTRIES_ORDER_PROP = "talend.fields.order";
Expand Down Expand Up @@ -127,6 +130,7 @@ public List<Entry> getMetadata() {

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public Stream<Entry> getAllEntries() {
return Stream.concat(this.metadataEntries.stream(), this.entries.stream());
}
Expand All @@ -146,12 +150,14 @@ public Builder toBuilder() {

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public List<Entry> getEntriesOrdered() {
return getAllEntries().sorted(entriesOrder).collect(toList());
}

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public EntriesOrder naturalOrder() {
return entriesOrder;
}
Expand Down Expand Up @@ -411,6 +417,7 @@ private EntryImpl(final EntryImpl.BuilderImpl builder) {

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public String getOriginalFieldName() {
return rawName != null ? rawName : name;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,18 +173,21 @@ public List<Entry> getMetadata() {

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public Stream<Entry> getAllEntries() {
return Stream.empty();
}

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public List<Entry> getEntriesOrdered() {
return emptyList();
}

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public Builder moveBefore(final String before, final String name) {
throw new UnsupportedOperationException("#moveBefore()");
}
Expand Down Expand Up @@ -251,6 +254,7 @@ public Builder remove(final Entry entry) {

@Override
@JsonbTransient
@jakarta.json.bind.annotation.JsonbTransient
public List<Entry> getEntriesOrdered(final Comparator<Entry> comparator) {
throw new UnsupportedOperationException("#getEntriesOrdered()");
}
Expand Down
23 changes: 23 additions & 0 deletions component-runtime-manager/module-knowledge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Module knowledge — component-runtime-manager

## JSON-B provider resolution

### `createPojoJsonbBuilder`'s Johnzon-specific reflection breaks when another JSON-B provider wins ServiceLoader resolution

[2026-09-22 | QTDI-3358] `DefaultServiceProvider.createPojoJsonbBuilder` calls plain
`javax.json.bind.JsonbBuilder.newBuilder()` and then reflectively reaches into a private
Johnzon-only `MapperBuilder builder` field (via `getDeclaredField("builder")`) to force
`setDoCloseOnStreams(true)`. This silently assumes Johnzon's `JohnzonBuilder` is always the
`JsonbProvider` that `ServiceLoader` resolves. That assumption breaks whenever another module on
the same classpath re-pins the JSON-B provider stack — e.g. `component-server`'s
`dependencyManagement` moving `johnzon-core`/`johnzon-mapper` to the jakarta line while still
needing a javax-line `javax.json.bind.spi.JsonbProvider` (QTDI-3358's jakarta migration pulled in
Yasson + `org.glassfish:javax.json` for that leftover javax lookup). Yasson's builder impl has no
`builder` field, so the reflection throws `NoSuchFieldException`, previously rethrown as a fatal
`IllegalStateException` and crashing any consumer that reaches this code path with a non-Johnzon
provider on the classpath. **Fix applied**: guard the reflective block behind
`jsonbBuilder instanceof org.apache.johnzon.jsonb.JohnzonBuilder` and skip the
`doCloseOnStreams` optimization (with a debug log) when a different provider is resolved, instead
of treating it as an error. Any future change to this method — or to a module's JSON-B
dependency wiring — should re-check which provider actually wins `ServiceLoader` resolution on
that module's classpath before assuming Johnzon-specific internals are reachable.
Original file line number Diff line number Diff line change
Expand Up @@ -541,20 +541,20 @@ public Path resolve(final String path) {
recordBuilderFactoryProvider, propertyEditorRegistry);
}

// Intentionally *not* referencing org.apache.johnzon.jsonb.JohnzonProvider directly: some downstream
// modules (e.g. component-server) pin johnzon-core/johnzon-jsonb to the jakarta.json.* line, whose
// classes no longer implement javax.json.bind.spi.JsonbProvider. A hardcoded "new JohnzonProvider()"
// whose declared return type is the javax interface fails class verification (VerifyError) as soon
// as this class is loaded, before the try/catch below ever runs. Going through the ServiceLoader-based
// JsonbProvider.provider() keeps this class link-safe regardless of which johnzon-core is on the
// classpath; whichever javax.json.bind.spi.JsonbProvider implementation is actually present wins.
private JsonbProvider loadJsonbProvider() {
try {
return new org.apache.johnzon.jsonb.JohnzonProvider();
} catch (final RuntimeException re) {
return JsonbProvider.provider();
}
return JsonbProvider.provider();
}

// See loadJsonbProvider() above - same rationale applies to javax.json.spi.JsonProvider.
private JsonProvider loadJsonProvider() {
try {
return new org.apache.johnzon.core.JsonProviderImpl();
} catch (final RuntimeException re) {
return JsonProvider.provider();
}
return JsonProvider.provider();
}

protected Supplier<Locale> getLocalSupplier() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import javax.json.stream.JsonGeneratorFactory;
import javax.json.stream.JsonParserFactory;

import org.apache.johnzon.jsonb.JohnzonBuilder;
import org.apache.johnzon.mapper.MapperBuilder;
import org.talend.sdk.component.api.record.RecordPointerFactory;
import org.talend.sdk.component.api.service.cache.LocalCache;
Expand Down Expand Up @@ -229,14 +230,22 @@ private JsonbBuilder createPojoJsonbBuilder(final String id, final Supplier<Json
emptyMap()),
jsonpReaderFactory))
.withConfig(jsonbConfig);
try { // to passthrough the writer, otherwise RecoderJsonGenerator is broken
final Field mapper = jsonbBuilder.getClass().getDeclaredField("builder");
if (!mapper.isAccessible()) {
mapper.setAccessible(true);
// to passthrough the writer, otherwise RecoderJsonGenerator is broken - only applies to the
// Johnzon JsonbBuilder implementation, other JSON-B providers (e.g. Yasson) don't expose this
// internal field and don't need the optimization
if (jsonbBuilder instanceof JohnzonBuilder) {
try {
final Field mapper = jsonbBuilder.getClass().getDeclaredField("builder");
if (!mapper.isAccessible()) {
mapper.setAccessible(true);
}
((MapperBuilder) mapper.get(jsonbBuilder)).setDoCloseOnStreams(true);
} catch (final NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
((MapperBuilder) mapper.get(jsonbBuilder)).setDoCloseOnStreams(true);
} catch (final Exception e) {
throw new IllegalStateException(e);
} else {
log.debug("JsonbBuilder implementation {} is not Johnzon's, skipping doCloseOnStreams optimization",
jsonbBuilder.getClass().getName());
}
return jsonbBuilder;
}
Expand Down
5 changes: 2 additions & 3 deletions component-server-parent/component-server-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@

<dependencies>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jaxrs_2.1_spec</artifactId>
<version>${geronimo-jaxrs.version}</version>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@
*/
package org.talend.sdk.component.server.api;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.microprofile.openapi.annotations.enums.ParameterIn.QUERY;
import static org.eclipse.microprofile.openapi.annotations.enums.SchemaType.OBJECT;
import static org.eclipse.microprofile.openapi.annotations.enums.SchemaType.STRING;

import java.util.Map;
import java.util.concurrent.CompletionStage;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;

import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
Expand Down
Loading
Loading