Skip to content

[roadmap] 005 — TIBCO EMS Transport #5

@oluies

Description

@oluies

Draft planning artifact generated via spec-kit — see specs/005-tibco-ems/ (spec, plan, research, data-model, contracts, quickstart, tasks).

⚠️ Unverified draft: authored without a build environment (no Gradle/JDK 17/network/TIBCO/DuckDB). On branch t/yolo-d76fe7 (not yet pushed).

Tasks (mirrors specs/005-tibco-ems/tasks.md)

Phase 1: Setup

  • T001 [P] Add the JMS API artifact (javax.jms/jakarta.jms matching the pinned EMS client namespace — research R7) to gradle/libs.versions.toml and wire it as a compile+test dependency in build.gradle.kts, pinned to a single version, Dependabot-tracked (FR-014, CONTRACT-LIB-5; research R1/R7) (needs build env)
  • T002 [P] Add the in-process non-TIBCO JMS test double dependency (test scope only — embeddable Apache-2.0 broker such as ActiveMQ Artemis, or a mocking library; final choice in T011) to gradle/libs.versions.toml + build.gradle.kts, pinned, Apache-2.0-compatible (FR-012/FR-014, CONTRACT-LIB-5; research R3) (needs build env)
  • T003 [P] Declare the externalized proprietary EMS jar (tibjms.jar + companions) via a build property pointing at a local/secured path in build.gradle.kts — runtime/integration scope only, never committed — mirroring the Rendezvous-jar externalization (FR-010/FR-014, CONTRACT-LIB-1; research R8) (needs build env)
  • T004 [P] Add the opt-in, environment-gated ems-integration test profile/flag in build.gradle.kts, skipped by default, so the default build/test runs without the EMS jar or a broker (FR-011, CONTRACT-LIB-2/4/6; research R3/R8) (needs build env)
  • T005 Create the package src/main/org/rvsnoop/transport/ems/ and src/test/org/rvsnoop/transport/ems/ (with Apache-2.0 license headers) for the adapter and its tests, plus src/test/org/rvsnoop/transport/ems/support/ for the JMS test double

Phase 2: Foundational (Blocking Prerequisites)

  • T006 Confirm feature 004's delivered transport port + registry + NeutralMessage API against the provisional surface assumed in plan.md/data-model.md (connect/subscribe/receive/close; registry kind registration; neutral field-tree API); record any signature deltas and re-check the contract/Constitution gate (FR-001/FR-015; research R2, CONTRACT-EMS-2) (blocked until 004 lands)
  • T007 Implement the jar-free compile seam: code the adapter against the standard JMS API only, and reach the EMS-specific ConnectionFactory (com.tibco.tibjms.TibjmsConnectionFactory or JNDI) reflectively/by configuration so the proprietary type never lands on the compile classpath, in src/main/org/rvsnoop/transport/ems/EmsConnectionFactoryProvider.java (FR-010/FR-011, CONTRACT-CONV-7/LIB-2; research R1) (needs build env to verify factory class/JNDI name)
  • T008 [P] Implement EmsDestination (name + kind enum {TOPIC, QUEUE}, default TOPIC) in src/main/org/rvsnoop/transport/ems/EmsDestination.java (FR-003; data-model)
  • T009 [P] Implement EmsConnection config type (id, serverUrl required, optional username/password/clientId, fixed transportKind "ems", bounded connect timeout) realizing 004's neutral connection, in src/main/org/rvsnoop/transport/ems/EmsConnection.java (FR-002; data-model, CONTRACT-EMS-1)
  • T010 [P] Implement EmsProviderAvailability runtime guard (guarded Class.forName of the EMS factory; clear "library must be provisioned" message when absent; never throw NoClassDefFoundError into the UI) in src/main/org/rvsnoop/transport/ems/EmsProviderAvailability.java (FR-016; research R9, CONTRACT-ERR-4)
  • T011 Implement the in-process non-TIBCO JMS test double (Connection/Session/MessageConsumer/QueueBrowser + the five Message subtypes, with injectable connect/auth/disconnect failures), in src/test/org/rvsnoop/transport/ems/support/FakeJmsProvider.java — the broker-free, jar-free harness for all Phase 3–5 tests (FR-012, CONTRACT-LIB-6; research R3) ([CLARIFY] mock vs embedded Artemis — research R3)

Phase 3: User Story 1 - Trace messages from an EMS topic (Priority: P1) 🎯 MVP

  • T012 [P] [US1] Connection lifecycle test (start → CONNECTED, selectable as source; stop → delivery ceases, sessions/consumers/connection released) in src/test/org/rvsnoop/transport/ems/EmsTransportTest.java (CONTRACT-EMS-1/3; FR-002/FR-009, SC-001) (needs build env)
  • T013 [P] [US1] Topic subscription test (messages published to a subscribed topic appear as records attributed to connection+topic) in src/test/org/rvsnoop/transport/ems/EmsSubscriptionTest.java (CONTRACT-SUB-1; FR-003/FR-004, SC-001) (needs build env)
  • T014 [P] [US1] JmsMessageConverter test covering all five body kinds + headers/properties + empty-body + very-large + uninterpretable body, run with the proprietary jar absent from the classpath (CONTRACT-CONV-1..7; FR-005/FR-006, SC-003) in src/test/org/rvsnoop/transport/ems/JmsMessageConverterTest.java (needs build env)
  • T015 [P] [US1] Core-field population test (subject/destination, timestamp, type, originating connection, sequence) consistent with other transports, in src/test/org/rvsnoop/transport/ems/JmsMessageConverterTest.java (CONTRACT-CONV-8; FR-007) (needs build env)
  • T016 [P] [US1] RV-coexistence test: an EMS connection (faked) and an RV connection both feed the same trace, each record attributed to its originating connection, RV behaviour unchanged, in src/test/org/rvsnoop/transport/ems/EmsCoexistenceTest.java (CONTRACT-EMS-4; FR-015, SC-006) (needs build env)
  • T017 [US1] Implement JmsMessageConverter (JMS → NeutralMessage): standard headers → headers subtree, user properties → properties subtree (typed); body by type — text→text field, bytes→raw-bytes, map→nested fields, stream→ordered fields, object→opaque raw bytes + marker (no Java deserialization); unknown/uninterpretable→raw bytes + marker, never throws; populate core fields, in src/main/org/rvsnoop/transport/ems/JmsMessageConverter.java (FR-005/FR-006/FR-007; data-model, CONTRACT-CONV-1..8) ([CLARIFY] object-body policy + exact header set — research R4)
  • T018 [US1] Implement EmsSubscription (destination, optional selector, mode enum {OBSERVE, CONSUME} default OBSERVE) in src/main/org/rvsnoop/transport/ems/EmsSubscription.java; topic path attaches a live MessageConsumer (FR-003; data-model)
  • T019 [US1] Implement EmsTransport connect/start (factory via T007 → Connection + ExceptionListener → Session(s) → Connection.start(); CONNECTED), topic subscribe (MessageConsumer + MessageListener → converter → existing event/ledger pipeline off the UI thread), and close (release consumers/sessions/connection), in src/main/org/rvsnoop/transport/ems/EmsTransport.java (FR-002/FR-004/FR-009; research R6, CONTRACT-EMS-1/3, CONTRACT-SUB-1)
  • T020 [US1] Add the additive Guice binding registering EmsTransport under transport kind "ems" in 004's registry (no core/UI/RV change) in src/main/org/rvsnoop/transport/ems/EmsTransportModule.java, and contribute the module to the application's injector (FR-001/FR-015, CONTRACT-EMS-2) (depends on T006 registry mechanism)
  • T021 [US1] Verify quickstart Scenario 1 (topic MVP), Scenario 2 (jar-free lossless conversion), Scenario 4 (RV coexistence), Scenario 5 (lifecycle/resource release) (SC-001/SC-003/SC-006) (needs build env)

Phase 4: User Story 2 - Trace messages from an EMS queue (Priority: P2)

  • T022 [P] [US2] Queue subscription test: enqueued messages appear as records attributed to connection+queue; topic is the default kind and queue is an explicit choice, in src/test/org/rvsnoop/transport/ems/EmsSubscriptionTest.java (CONTRACT-SUB-2/3; FR-003/FR-004, SC-002) (needs build env)
  • T023 [P] [US2] Non-destructive-default test: a queue subscription in OBSERVE mode leaves the queue's messages intact (browse semantics); CONSUME mode used only on explicit opt-in, in src/test/org/rvsnoop/transport/ems/EmsQueueObserveTest.java (CONTRACT-SUB-4; FR-013, SC-002) (needs build env)
  • T024 [P] [US2] Duplicate-subscription test: subscribing to the same destination twice on one connection is rejected with a clear message or de-duplicated (no double-delivery surprises), in src/test/org/rvsnoop/transport/ems/EmsSubscriptionTest.java (CONTRACT-SUB-5; edge case) (needs build env)
  • T025 [US2] Extend EmsTransport subscribe to resolve queue destinations: QUEUE + OBSERVE → non-destructive QueueBrowser (default); QUEUE + CONSUME → MessageConsumer only on explicit opt-in, in src/main/org/rvsnoop/transport/ems/EmsTransport.java (FR-003/FR-013; research R5, CONTRACT-SUB-2/4) ([CLARIFY] queue observe semantics: browse vs non-ack consume — research R5)
  • T026 [US2] Implement duplicate-destination handling on a connection (reject-with-message or de-duplicate) in src/main/org/rvsnoop/transport/ems/EmsTransport.java (CONTRACT-SUB-5; edge case)
  • T027 [US2] Verify quickstart Scenario 3 (queue traced non-destructively; topic default honoured) (SC-002) (needs build env)

Phase 5: User Story 3 - Provide the EMS client library without committing it (Priority: P3)

  • T028 [P] [US3] Runtime-missing-library test: with the EMS provider absent at runtime, attempting an EMS connection yields a clear "must be provisioned" message and other transports stay usable — no uncaught NoClassDefFoundError reaches the UI, in src/test/org/rvsnoop/transport/ems/EmsErrorHandlingTest.java (CONTRACT-ERR-4; FR-016, SC-007) (needs build env)
  • T029 [P] [US3] Repository-inspection check: the proprietary EMS jar appears nowhere in version control (verifiable now) — assert via a build/CI check or test in src/test/org/rvsnoop/transport/ems/EmsLibraryAbsenceTest.java (CONTRACT-LIB-1; FR-010, SC-005)
  • T030 [P] [US3] Default-build / broker-free-suite assertion: the complete EMS suite (lifecycle, subscription, conversion, error handling) compiles and passes with the EMS jar absent and no live broker (CONTRACT-LIB-2/6; FR-011/FR-012, SC-004/SC-005) — gate in CI default stage (needs build env)
  • T031 [US3] Wire CI to obtain the EMS jar through a secured mechanism (not version control) for the ems-integration stage only; ensure absence of the secured source does not break the default build/test stages, in .github/workflows/*.yml (feature 001 files) (FR-010/FR-011, CONTRACT-LIB-4; research R8) (needs build env)
  • T032 [US3] Verify quickstart Scenario 7 (jar-free build + broker-free suite) and Scenario 8 (jar never committed; secured CI provisioning) (SC-004/SC-005) (needs build env)
  • T033 [US3] ⚙ Integration smoke (opt-in ems-integration profile): provision the EMS jar, connect to a reachable EMS server, subscribe to a real topic and queue, confirm runtime availability and tracing parity with the faked scenarios — quickstart Scenario 9; skipped by default (CONTRACT-LIB-3; FR-010, SC-005) (needs broker/jar)

Phase 6: Polish & Cross-Cutting Concerns

  • T034 [US?] Error & stability handling for unreachable/wrong address (bounded connect timeout — no hang), authentication failure, and mid-trace disconnect via the ExceptionListener (clear actionable messages, stopped/error state, trace uncorrupted, no crash) in src/main/org/rvsnoop/transport/ems/EmsTransport.java, with tests in src/test/org/rvsnoop/transport/ems/EmsErrorHandlingTest.java (CONTRACT-ERR-1/2/3; FR-008, SC-007; research R6) (needs build env)
  • T035 [P] Update README/developer docs: a second EMS transport, how to provision the proprietary jar externally (never committed), the default broker-free test path, and the opt-in ems-integration profile (FR-010/FR-016)
  • T036 [P] Update dependency/governance notes: JMS API + test double added (Apache-2.0-compatible, pinned, Dependabot-tracked); proprietary EMS jar externalized and pinned — reaffirms Principles I, IV, V (FR-014, CONTRACT-LIB-5)
  • T037 Run the full quickstart.md validation (Scenarios 1–8; Scenario 9 only if the integration profile is provisioned), confirming zero crashes across all error scenarios (SC-001..SC-007) (needs build env; Scenario 9 needs broker/jar)

Metadata

Metadata

Assignees

No one assigned

    Labels

    roadmapSpec-kit roadmap feature (draft plan)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions