Skip to content

Latest commit

 

History

History
164 lines (117 loc) · 6.7 KB

File metadata and controls

164 lines (117 loc) · 6.7 KB

observer-java

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(...), and expect(...)
  • 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 observe dispatch 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.xml for Maven projects
  • build.gradle plus settings.gradle for Gradle projects
  • module-info.java for 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.

Files

  • HOWTO.md: detailed user manual covering authoring, determinism, host transport, inventory derivation, and end-to-end workflow
  • src/main/java/io/frogfish/observer/Observer.java: public API
  • src/test/java/io/frogfish/observer/ObserverSelfTest.java: stdlib-only self-test coverage for the Java SDK
  • Makefile: local build, self-test, and example commands using javac
  • pom.xml: Maven build metadata with source/javadoc jars and the stdlib self-test bound into test
  • build.gradle: Gradle build metadata with check wired to the stdlib self-test
  • settings.gradle: Gradle project identity
  • ../java-junit5/: optional JUnit 5 bridge for teams that want to export ordinary @Test methods through Observer
  • examples/example-smoke/: tiny collection and execution example
  • examples/host-example/: tiny direct list and run host example
  • examples/host-embed-example/: own-main style observe namespace example
  • starter/: runnable project-shaped example with Java build, provider host build, inventory derivation, suite run, and snapshot verification
  • starter-embedded/: runnable app-shaped example where the application keeps its own CLI and routes observe ...
  • 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/.

Minimal Shape

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"));

Validation Rules

  • 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.

Self-Test Suite

cd lib/java
make test

If you prefer native Java build tools, the equivalent commands are:

cd lib/java
mvn test
gradle check

The repository does not assume Maven or Gradle are installed locally, so the checked validation path remains make test.

Smoke Example

cd lib/java
make example-smoke

Host Example

cd 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 1000

The 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.

Own Main Integration

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.

Optional JUnit 5 Bridge

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
  • @ObserverId gives an explicit stable override when you want one
  • @DisplayName only affects the local authored title, not the deterministic exported target unless you opt into @ObserverId
  • the bridge is deliberately scoped to ordinary zero-argument @Test methods plus @BeforeEach and @AfterEach

Packaging

For local artifact generation inside this repo, use:

cd lib/java
make jar
make javadoc

That produces a plain jar and Javadoc output without depending on Maven or Gradle.