Minimal Java-facing provider micro-library for Observer.
If you are new to this surface, start with HOWTO.md before reading the individual snippets and starters.
This aims at the same DX bar as the Go, Python, Rust, C, and .NET libraries:
- authors write
describe(...),test(...),it(...), andexpect(...) - default identity is derived deterministically from explicit suite path plus test title
- optional
id(...)provides a refactor-stable override when needed - observation is bounded and opt-in through
ctx.observe().* - direct host and embedded
observedispatch are owned by the library
The common path stays human-first. The deterministic boundary stays explicit.
For Java, the intended style is static import:
import static io.frogfish.observer.Observer.*;The folder now also carries normal Java build metadata:
pom.xmlfor Maven projectsbuild.gradleplussettings.gradlefor Gradle projectsmodule-info.javafor JPMS-aware consumers
The publish metadata is now release-oriented as well: license, SCM, issue tracker, developer identity, source jar, Javadoc jar, and optional signing hooks are all declared for both Maven and Gradle flows.
The repo still validates the SDK with plain javac so the in-repo workflow stays toolchain-light, but Java consumers no longer need to invent packaging metadata from scratch.
HOWTO.md: detailed user manual covering authoring, determinism, host transport, inventory derivation, and end-to-end workflowsrc/main/java/io/frogfish/observer/Observer.java: public APIsrc/test/java/io/frogfish/observer/ObserverSelfTest.java: stdlib-only self-test coverage for the Java SDKMakefile: local build, self-test, and example commands usingjavacpom.xml: Maven build metadata with source/javadoc jars and the stdlib self-test bound intotestbuild.gradle: Gradle build metadata withcheckwired to the stdlib self-testsettings.gradle: Gradle project identity../java-junit5/: optional JUnit 5 bridge for teams that want to export ordinary@Testmethods through Observerexamples/example-smoke/: tiny collection and execution exampleexamples/host-example/: tiny directlistandrunhost exampleexamples/host-embed-example/: own-main styleobservenamespace examplestarter/: runnable project-shaped example with Java build, provider host build, inventory derivation, suite run, and snapshot verificationstarter-embedded/: runnable app-shaped example where the application keeps its own CLI and routesobserve ...starter-failure/: runnable failing companion showing the same provider flow with one intentionally failing exported test
If you want the full end-to-end workflow rather than isolated snippets, start with starter/, then read starter-embedded/, and then compare those with starter-failure/.
import static io.frogfish.observer.Observer.*;
var tests = collectTests(() -> {
describe("database", () -> {
test("access to the database", ctx -> {
ctx.stdout("ok\n");
expect(true).toBeTruthy();
});
});
});When id(...) is omitted, Observer derives a deterministic identity from suite path, test title, and duplicate occurrence order.
If a test wants a refactor-stable identity, it opts into id(...) explicitly:
test("access to the database", ctx -> {
expect(true).toBeTruthy();
}, id("database/access"));If a test wants to emit observational data, it uses the author context directly:
test("access to the database", ctx -> {
var observe = ctx.observe();
observe.metric("wall_time_ns", 104233.0);
observe.vector("request_latency_ns", java.util.List.of(1000.0, 1100.0, 980.0));
observe.tag("resource_path", "fixtures/config.json");
expect(true).toBeTruthy();
}, id("database/access"));- explicit
id(...), when present, must be non-empty - resolved canonical identities must be unique
- resolved targets must be unique
- deterministic sorting is by canonical name, then target
In this first cut, the resolved identity is used for both canonical name and target.
cd lib/java
make testIf you prefer native Java build tools, the equivalent commands are:
cd lib/java
mvn test
gradle checkThe repository does not assume Maven or Gradle are installed locally, so the checked validation path remains make test.
cd lib/java
make example-smokecd lib/java
javac --release 17 -d build/lib-classes $(find src/main/java -name '*.java')
javac --release 17 -cp build/lib-classes -d build/example-classes $(find examples/host-example/src/main/java -name '*.java')
java -cp build/lib-classes:build/example-classes io.frogfish.observer.examples.HostExample list
java -cp build/lib-classes:build/example-classes io.frogfish.observer.examples.HostExample observe --target pkg::smoke --timeout-ms 1000
java -cp build/lib-classes:build/example-classes io.frogfish.observer.examples.HostExample run --target pkg::fail --timeout-ms 1000The library owns the standard provider host transport for Java too. A direct host can stay nearly trivial through hostMain(...).
For developer-facing usage, prefer observe. run remains available for compatibility with the standardized outer provider contract.
If a project already owns its CLI, the library can also serve an embedded observe namespace. The runnable example lives in examples/host-embed-example/, and the full app-shaped workflow lives in starter-embedded/.
If you want the closest thing in this repo to a normal external Java application, start with ../../examples/java-consumer-maven/ for a Maven app, ../../examples/java-consumer-gradle/ for a Gradle app, or ../../examples/java-consumer/ for the plain javac fallback used for repo-local validation.
The optional bridge in ../java-junit5/ is for teams that already author tests as JUnit 5 methods and want an adoption path that does not force a full rewrite on day one.
The bridge intentionally keeps the deterministic Observer boundary explicit:
- exported identity defaults to
fully.qualified.ClassName#methodName @ObserverIdgives an explicit stable override when you want one@DisplayNameonly affects the local authored title, not the deterministic exported target unless you opt into@ObserverId- the bridge is deliberately scoped to ordinary zero-argument
@Testmethods plus@BeforeEachand@AfterEach
For local artifact generation inside this repo, use:
cd lib/java
make jar
make javadocThat produces a plain jar and Javadoc output without depending on Maven or Gradle.