From ca13807d8d508a2e053a8c7e577760e6e46f2343 Mon Sep 17 00:00:00 2001 From: Karthik Date: Fri, 31 Jul 2026 12:43:30 -0400 Subject: [PATCH 1/2] Add self-contained ai.chalk:chalk-java-shaded artifact Relocates all third-party deps (protobuf, grpc, jackson, arrow, netty, ...) into ai.chalk.shaded.* so the artifact is immune to whatever versions a consumer or their runtime pins. Published as a separate coordinate with a dependency-free POM (deps are bundled, not declared), so consumers never pull a conflicting transitive. Fixes NoClassDefFoundError for clients pinned below protobuf 3.25 (e.g. 3.19 lacking MapFieldReflectionAccessor). --- build.gradle | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/build.gradle b/build.gradle index 0f51f9c..d4fb02a 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ plugins { id "signing" id "io.github.gradle-nexus.publish-plugin" version "1.3.0" id 'com.vanniktech.maven.publish' version '0.30.0' + id 'com.gradleup.shadow' version '8.3.5' } group 'ai.chalk' @@ -47,6 +48,35 @@ dependencies { testImplementation 'org.slf4j:slf4j-simple:2.0.9' } +// Self-contained "shaded" jar: relocates ALL third-party deps into ai.chalk.shaded.* so the artifact +// is immune to whatever versions a consumer (or their Flink runtime) pins. Flink is compileOnly, so +// it is not bundled (stays provided by the cluster). Publish as the `shaded` classifier. +shadowJar { + archiveClassifier = 'shaded' + mergeServiceFiles() // rewrite META-INF/services (grpc providers, jackson, etc.) to shaded names + + // slf4j-api is the logging facade — let the runtime (Flink) own it exclusively, so there's no + // facade version skew (1.x vs 2.x binding). chalk-java's org.slf4j refs bind to the runtime's. + dependencies { exclude(dependency('org.slf4j:slf4j-api')) } + + relocate 'com.google', 'ai.chalk.shaded.google' // protobuf, guava, gson, flatbuffers, api-protos + relocate 'io.grpc', 'ai.chalk.shaded.grpc' // incl. grpc-netty-shaded + relocate 'io.netty', 'ai.chalk.shaded.netty' // arrow-memory-netty (pure JVM here) + relocate 'io.perfmark', 'ai.chalk.shaded.perfmark' + relocate 'com.fasterxml.jackson', 'ai.chalk.shaded.jackson' + relocate 'org.apache.arrow', 'ai.chalk.shaded.arrow' + relocate 'org.apache.commons', 'ai.chalk.shaded.commons' // codec, io, compress, lang3 + relocate 'org.yaml.snakeyaml', 'ai.chalk.shaded.snakeyaml' + relocate 'org.checkerframework', 'ai.chalk.shaded.checkerframework' + relocate 'org.codehaus.mojo', 'ai.chalk.shaded.mojo' + // Intentionally NOT relocated: + // ai.chalk.* -> the public API (consumers import these) + // org.slf4j -> logging facade; must bind to the runtime's logging + // javax.annotation -> standard annotations, no conflict + // com.github.luben -> zstd-jni: native JNI, relocating its package breaks the .so binding + // org.apache.flink.* -> provided by the cluster (compileOnly, not bundled) +} + test { useJUnitPlatform() jvmArgs '--add-opens=java.base/java.nio=ALL-UNNAMED' @@ -124,4 +154,22 @@ mavenPublishing { url = 'https://github.com/chalk-ai/chalk-java/' } } +} + +// Second coordinate: ai.chalk:chalk-java-shaded. Identical classes to chalk-java, but every +// third-party dependency is relocated into ai.chalk.shaded.* (see the shadowJar block). Its POM is +// intentionally DEPENDENCY-FREE — the deps live inside the jar — so a consumer pinned to older +// protobuf/grpc/jackson/arrow/netty never gets a conflicting transitive pulled in. Built as a manual +// publication (not `from components.java`), so no dependencies are written to the POM. +publishing { + publications { + shaded(MavenPublication) { + artifactId = 'chalk-java-shaded' + // Publish the shaded jar as the module's main artifact (strip the 'shaded' classifier). + artifact(tasks.shadowJar) { classifier = null } + // POM name/license/scm/developers are applied to all publications by the vanniktech + // mavenPublishing block below. No are declared here on purpose — every + // third-party dep is bundled + relocated inside the jar. + } + } } \ No newline at end of file From 682738aa1a976324fed4cddcce226db0c4d2f900 Mon Sep 17 00:00:00 2001 From: Karthik Date: Thu, 6 Aug 2026 12:06:53 -0400 Subject: [PATCH 2/2] Declare slf4j-api in chalk-java-shaded POM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bundled arrow/netty classes reference org.slf4j, which is not relocatable (logging facade must bind to the runtime). Declaring slf4j-api guarantees the class is present for any consumer, so the artifact is truly self-contained; Flink parent-first-loads org.slf4j so its own version still wins on-cluster. It is the only declared dep — everything else is bundled + relocated. --- build.gradle | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index d4fb02a..39a7d39 100644 --- a/build.gradle +++ b/build.gradle @@ -157,10 +157,10 @@ mavenPublishing { } // Second coordinate: ai.chalk:chalk-java-shaded. Identical classes to chalk-java, but every -// third-party dependency is relocated into ai.chalk.shaded.* (see the shadowJar block). Its POM is -// intentionally DEPENDENCY-FREE — the deps live inside the jar — so a consumer pinned to older -// protobuf/grpc/jackson/arrow/netty never gets a conflicting transitive pulled in. Built as a manual -// publication (not `from components.java`), so no dependencies are written to the POM. +// third-party dependency is relocated into ai.chalk.shaded.* (see the shadowJar block). Its POM +// declares only slf4j-api (a non-relocatable logging facade) — every other dep lives inside the jar, +// so a consumer pinned to older protobuf/grpc/jackson/arrow/netty never gets a conflicting transitive +// pulled in. Built as a manual publication (not `from components.java`) to keep the POM minimal. publishing { publications { shaded(MavenPublication) { @@ -168,8 +168,20 @@ publishing { // Publish the shaded jar as the module's main artifact (strip the 'shaded' classifier). artifact(tasks.shadowJar) { classifier = null } // POM name/license/scm/developers are applied to all publications by the vanniktech - // mavenPublishing block below. No are declared here on purpose — every - // third-party dep is bundled + relocated inside the jar. + // mavenPublishing block below. The ONLY declared dependency is slf4j-api: bundled + // arrow/netty classes reference org.slf4j, but it is not relocatable (it's a logging + // facade that must bind to the runtime's logging). Declaring it guarantees the class is + // present for any consumer; in a Flink runtime, Flink parent-first-loads org.slf4j, so + // its own version always wins regardless of what we declare. Everything else is bundled + + // relocated inside the jar, so no other (conflict-prone) dependency is declared. + pom.withXml { + def deps = asNode().appendNode('dependencies') + def slf4j = deps.appendNode('dependency') + slf4j.appendNode('groupId', 'org.slf4j') + slf4j.appendNode('artifactId', 'slf4j-api') + slf4j.appendNode('version', '2.0.9') + slf4j.appendNode('scope', 'runtime') + } } } } \ No newline at end of file