Java-specific requirements for the Market Data Java SDK. These derive from architectural decisions recorded in ADRs and supplement, not replace, the cross-language Market Data SDK Requirements. When this document conflicts with the generic requirements doc, this document wins for the Java SDK only.
Each requirement section cites the ADR it derives from. New requirements should be added here only after the corresponding ADR is accepted.
| Section | Source ADR |
|---|---|
| §1 Distribution | ADR-001 |
| §2 Kotlin Interoperability | ADR-001 |
| §3 JDK Targets | ADR-002 |
| §4 HTTP Client | ADR-004 |
| §5 Build Tool | ADR-003 |
| §6 JSON Library | ADR-005 |
| §7 Async API Surface | ADR-006 |
Source: ADR-001
- The SDK is published as a single Java artifact.
- Sources are Java only — no Kotlin sources in the build.
- The published JAR must not bring
kotlin-stdlibas a transitive dependency. JSpecify annotations are compile-time only and do not count. - A separate Kotlin extensions artifact (Option E in ADR-001) is out of scope for the initial release. Revisit only if Kotlin demand for ergonomics beyond the interop checklist materializes after launch.
Source: ADR-001
The SDK is published as Java only, but Kotlin consumers are a first-class audience served via Java interop. This mirrors the JavaScript SDK's TypeScript story: one artifact, with type metadata that makes the second-language audience first-class.
The following requirements apply to the entire public API surface.
- Annotate every public type, parameter, return, and field with JSpecify nullability annotations.
- Apply
@NullMarkedat the package level (viapackage-info.java) so non-null is the default; mark nullable items explicitly with@Nullable. - Without these annotations, Kotlin sees Java values as platform types
(
String!) and Kotlin's null safety silently breaks.
Do not use any of the following as public method or parameter names — they force Kotlin callers to wrap calls in backticks:
object, is, in, fun, when, as, val, var, typealias,
interface, package, typeof, out, super
Java getters become Kotlin properties at the call site: getFoo() is
callable as .foo, isFoo() as .foo. Therefore:
- Do not perform expensive work, network/disk I/O, or anything with observable side effects in a getter — Kotlin users will treat it as a field read.
- Use consistent getter naming (
getFoofor objects,isFoofor booleans) so Kotlin sees a clean property name.
Single-abstract-method (SAM) Java interfaces auto-convert to Kotlin lambdas. For any callback or listener interface in the public API:
- Keep it to a single abstract method (no
defaultsecond method). - Prefer
java.util.function.*types (Consumer,Function,Predicate,Supplier) where applicable — Kotlin treats these as lambda-compatible out of the box.
- Use
? extends Ton producer parameters and? super Ton consumer parameters in public APIs. - Missing wildcards translate to invariant Kotlin types and produce awkward call sites.
- Return standard JVM collection types:
List<T>,Map<K,V>,Set<T>. Do not return arrays for variable-length results. - Return empty collections, never
null. - Do not use
Optional<T>as a field type or parameter type.Optional<T>is acceptable only as a return type on Java-facing methods; Kotlin callers prefer nullable returns.
- Public async methods return
CompletableFuture<T>(per the generic requirements doc, §11.2). - Do not depend on
kotlinx-coroutinesfrom the SDK. Kotlin consumers bridge viaCompletableFuture.await()fromkotlinx-coroutines-jdk8.
The README and per-method docs must include at least one Kotlin usage example alongside the Java example for the quick-start path:
val client = MarketDataClient(
apiKey = "KEY",
baseUrl = null,
apiVersion = null,
validateOnStartup = true,
)
val quote = client.stocks().quote("AAPL")
println(quote)Source: ADR-002
- Minimum runtime: JDK 17 (LTS). Consumers must run JDK 17 or newer.
- Build target: the published artifact is compiled with
javac --release 17so its bytecode runs on any JDK 17+ runtime. - CI test matrix: unit tests run on JDK 17, 21, and 25 to catch forward-compat regressions. Mirrors the multi-version testing approach used in the Python SDK.
- No multi-release JAR. Single bytecode level for the entire artifact.
- Minimum supported JDK must be documented in the README.
Source: ADR-004
- The SDK uses
java.net.http.HttpClientfrom the JDK standard library exclusively. - No third-party HTTP client (OkHttp, Apache HttpClient, etc.) may be added as a runtime dependency.
- HTTP/2 must be enabled (it is the default for
java.net.http). - A single shared
HttpClientinstance perMarketDataClientprovides automatic connection pooling (per generic SDK requirements doc §1.1). - Timeouts must satisfy the generic SDK requirements doc §10 (99-second request timeout, 2-second connect timeout).
- Async methods return
CompletableFuture<T>directly fromHttpClient.sendAsync— no adapter layer.
Source: ADR-003
- The SDK is built with Gradle using the Kotlin DSL
(
build.gradle.kts,settings.gradle.kts). - Dependency versions are managed via a Gradle version catalog
(
gradle/libs.versions.toml). - Standard plugins:
java-library,maven-publish, Vanniktech Maven Publish (or Gradle Nexus Publish) for Maven Central, Spotless for formatting, JaCoCo for coverage. - Integration tests live in a separate Gradle source set
(
integrationTest), gated by environment variable per generic SDK requirements doc §13.
Source: ADR-005
- The SDK uses Jackson (
jackson-databind) as its JSON library. - Records-based response models use Jackson's record support (Jackson 2.12+). Records are the default model shape (per §3 — JDK 17 minimum).
- The API's compressed parallel-arrays wire format (generic SDK
requirements doc §11.1) is decoded via custom Jackson
JsonDeserializerclasses. - The minimum Jackson version is documented in the published POM and README.
- The Jackson dependency is not shaded in v1. Shading is held in reserve as a mitigation if classpath collisions become a real customer pain post-launch.
Source: ADR-006
-
Every public endpoint method exposes both a sync and an async variant:
Quote quote = client.stocks().quote("AAPL"); CompletableFuture<Quote> quoteFuture = client.stocks().quoteAsync("AAPL");
-
Async method names use the
<methodName>Asyncconvention consistently. -
Async methods return
CompletableFuture<T>(matching the generic SDK requirements doc's Java async pattern). -
Internal logic is async-first. Sync methods are thin wrappers that call
.join()on the async path and unwrapCompletionExceptionto surface the underlying cause directly. -
Both surfaces share the same validation, retry, rate-limit, and concurrency-pool logic — no parallel implementations.
-
Test coverage must include both sync and async variants for every endpoint.
- No Kotlin sources in the build
- Published JAR has no
kotlin-stdlibtransitive dependency
- JSpecify nullability annotations applied to entire public API
(
@NullMarkedpackage-level +@Nullablewhere applicable) - No Kotlin reserved words in public method or parameter names
- Public callback/listener interfaces are SAM (single abstract method)
- No expensive work or I/O in property-style getters
- No
Optional<T>in fields or parameters - Generic wildcards correct on public APIs (
? extends/? superwhere appropriate) - Kotlin usage example in README quick-start
- Build pipeline uses
javac --release 17 - CI runs unit tests on JDK 17, 21, and 25
- Minimum supported JDK documented in README
- Published artifact is not a multi-release JAR
- All HTTP requests go through
java.net.http.HttpClient - No third-party HTTP client on the dependency tree
- Single shared
HttpClientinstance perMarketDataClient - HTTP/2 enabled
- 99-second request timeout / 2-second connect timeout configured
- Async methods return
CompletableFuture<T>natively
- Build uses Gradle with the Kotlin DSL (
.gradle.kts) - Dependency versions managed via
gradle/libs.versions.toml - Integration tests live in a separate
integrationTestsource set - Standard plugins applied:
java-library,maven-publish, Maven Central publish plugin, Spotless, JaCoCo
- Jackson is the only JSON dependency on the dependency tree
- Wire-format decoders implemented as custom
JsonDeserializerclasses - Records-based response models use Jackson's record support
- Minimum Jackson version documented in POM and README
- Jackson is not shaded into the SDK JAR (v1)
- Every endpoint method has both sync and
<methodName>Asyncvariants - Async methods return
CompletableFuture<T> - Internal logic is async-first; sync methods wrap
.join()and unwrapCompletionException - Both surfaces share validation, retry, rate-limit, and concurrency-pool logic
- Tests cover both sync and async paths for every endpoint