Files
session-java/README.md
T
selimaj-devandclaude 97eab5f814
Gradle Build / build (pull_request) Failing after 6s
Fix lost responses and close handling; publish to Gitea as 0.2.0
- Register pending requests before sending so fast responses aren't lost
- Fail pending requests with SessionClosedException when the connection
  closes or errors, and add onClose(), isOpen() and a request overload
  with a timeout
- Serialize text sends: java.net.http.WebSocket allows only one
  outstanding send, so concurrent requests could throw
- Reassemble text messages delivered in parts before parsing
- Complete error responses with SessionErrorException carrying the payload
- Answer requests for unknown methods with an error response
- Fix SessionResult.error() producing success responses
- Fix close() using invalid close code 0; send 1000
- Publish as dev.selimaj:session-java to the Gitea package registry;
  make Jackson an api dependency and drop unused Tyrus/Jakarta deps
- Replace the empty test with 9 tests against an in-process server

Fixes #1

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 14:28:04 +02:00

3.2 KiB

Session

session-java

A lightweight, async WebSocket protocol for Java.

Introduction

This library provides type-safe WebSocket communication with a request-response and notification system built on top of a flexible protocol.
It ensures compile-time guarantees for message structure, reduces runtime errors, and simplifies building Java client/server applications.

  • Dynamic Methods: Each message includes a method enum for type safety.
  • Typed Requests & Responses: Automatic serialization and deserialization.
  • Optional Notifications: Send asynchronous notifications across sessions.

Features

  • Fully typed WebSocket sessions
  • Type-safe request/response mechanism
  • Typed notifications
  • Lightweight, minimal runtime overhead

Installation

session-java is published to the Gitea package registry at git.selimaj.dev.

repositories {
    maven { url 'https://git.selimaj.dev/api/packages/selimaj-dev/maven' }
}

dependencies {
    implementation 'dev.selimaj:session-java:0.2.0'
}

Versions up to 0.1.6 were published through JitPack as com.github.selimaj-dev:session-java.

Usage

static final Method<String, String, String> DATA =
        new Method<>("data", String.class, String.class, String.class);

Session session = Session.connect("ws://localhost:8080/", 10, TimeUnit.SECONDS);

// Handle requests and notifications from the server.
session.onRequest(DATA, (id, data) -> SessionResult.ok("echo: " + data));
session.onNotification(DATA, data -> System.out.println("notified: " + data));

// Find out when the connection ends, from either side.
session.onClose(() -> System.out.println("disconnected"));

// Send a request, with or without a deadline.
String reply = session.request(DATA, "Hello", Duration.ofSeconds(10)).get();

session.close();

Semantics

  • A request completes exceptionally with:
    • SessionErrorException when the peer answers with an error response (getError() has the payload)
    • SessionClosedException when the session closes before the response arrives
    • TimeoutException when the timeout passes, if one was given
  • onClose handlers run exactly once, whether the session was closed locally, by the peer, or by a network failure. A handler registered after close runs immediately.
  • Sends are serialized, so the session is safe to use from multiple threads.
  • A request for an unknown method is answered with an error response instead of no reply.

Publishing

GITEA_TOKEN=<token with package:write> ./gradlew publish

Protocol

Every message is a JSON text frame with a type tag.

Request

The id is chosen by the sender and increments per peer.

{ "type": "request", "id": 1, "method": "data", "data": "Hello from client" }

Response

A response must carry the id of the request it answers.

{ "type": "response", "id": 1, "result": "Hello from server" }

Error response

{ "type": "errorresponse", "id": 1, "error": "Invalid data" }

Notification

A notification is fire-and-forget and gets no response.

{ "type": "notification", "method": "data", "data": "Hello from server" }