This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
moleculer-java-httpclient is a small, asynchronous HTTP/WebSocket client library (not an application) for the Java Moleculer ecosystem. It wraps AsyncHttpClient (AHC) 3.0.13 and exposes a Promise-based API that speaks Moleculer's data types. Published to Maven Central as com.github.berkesa:moleculer-java-httpclient, version 2.1.0. Bytecode target Java 17 (<release>17</release>); minimum consumer runtime: JDK 17 (Spring 6 transitive). Build JDK 17+ (JDK 25 in use). Single package: services.moleculer.httpclient.
The project builds with Maven (Java 17). There is no wrapper — use a locally installed mvn.
- Build + run tests + install to
~/.m2:mvn clean install - Full check (compile + tests):
mvn clean verify - Compile only:
mvn clean compile - Run tests:
mvn test - Run the single test class:
mvn test -Dtest=HttpClientTest - Release build (sources + javadoc + GPG sign + Central Portal publish):
mvn -Prelease deploy(seecoordination/MAVEN-CENTRAL-PUBLISHING.md)
- Netty version is pinned to
4.2.17.Final.moleculer-java-webbrings a standalone Netty server at4.2.17.Final, while AHC3.0.13bundles Netty4.2.17.Finaltoo. Thepom.xmlimportsio.netty:netty-bom:4.2.17.Finalin<dependencyManagement>so everyio.netty:*artifact resolves to one version — the embeddedApiGateway/NettyServerand the HTTP client share a single Netty on the port-8080 integration test. Verify withmvn dependency:tree. HttpClientTestis an end-to-end integration test, not a unit test. Its@BeforeEach setUp()boots a real embedded Moleculer app — aServiceBrokerwith a NettyApiGatewaylistening on port 8080 — then exercises the client against it over real HTTP and WebSocket;@AfterEach tearDown()stops both. Port 8080 must be free:setUp()probes it andassumeTrue(...)-skips the whole test (keepingmvn verifygreen) when it cannot bind. One large method covers all HTTP verbs, streaming, thetransferTo(...)targets, and WebSockets. Thenio-multipart-parserdependency is declared at test scope because the embeddedApiGatewayneeds it at runtime (web ships it asoptional).
The whole library funnels through one class and one execution path. Read these to understand it: HttpClient, RequestParams, ResponseHandler, WebSocketConnection.
- Requests/responses are
io.datatree.Tree(a JSON-like dynamic structure), not POJOs orJsonNode. - Async results are
io.datatree.Promise(.then(...).catchError(...), plus blocking.waitFor(...)), notCompletableFuture. - Binary streaming uses Moleculer's
services.moleculer.stream.PacketStream(from themoleculer-java-web/ core dependency), notInputStream.
HttpClient extends DefaultAsyncHttpClientConfig.Builder. This is the key, non-obvious design choice: the client is the AHC config builder. You configure transport options by calling inherited builder methods directly on the HttpClient instance, then call start() to build the underlying DefaultAsyncHttpClient. Always start() before use and stop() to release resources (a finalize() safety net also closes them).
All HTTP verb methods (get/post/put/delete/patch/head/options/connect/trace) are thin overloads that converge on the single private execute(url, method, Consumer<RequestParams>). execute():
- Builds a
RequestParams(which extends AHC'sRequestBuilderBase), applies the signature calculator and the caller's configurator. - Picks a default
ResponseHandlerif none was set —ResponseToJsonnormally,ResponseToBytesifreturnAsByteArray()was requested. - Wraps AHC's callback-style
AsyncHandlerin aPromise, bridging callbacks → resolve/reject.
The verb overloads wrap the user's Consumer<RequestParams> in one of two adapters before passing it to execute():
TreeConfigurator— forTreebodies. For POST-like verbs it serializes the Tree to a JSON body; for GET-like verbs it converts the Tree into URL query params.PacketStreamConfigurator— forPacketStreambodies (chunked upload, or fixed length if a content-length is given viaPacketStreamBodyGenerator).
RequestParams also offers output redirection via transferTo(...) overloads (OutputStream, WritableByteChannel, PacketStream, or a raw AHC AsyncHandler) and flags returnStatusCode() / returnHttpHeaders() / returnAsByteArray().
ResponseHandler (abstract, implements AsyncHandler<Tree>) is the base for the response-parsing strategies: ResponseToBytes → ResponseToJson (default), plus ResponseToOutputStream and ResponseToPacketStream for streaming responses elsewhere. When returnStatusCode/returnHttpHeaders are enabled, status and headers are written into the response Tree's Meta section as $status (int) and $headers (map) — e.g. rsp.getMeta().get("$status", 0). Header keys keep the server's casing (the gateway sends Content-Type / Content-Length capitalized). The body Tree itself stays clean otherwise.
AHC 3.x note: unlike AHC 2.x, AsyncHttpClient 3.x does not call
onBodyPartReceivedfor an empty response body, soResponseToBytes.bytescan benullatonCompleted().ResponseToBytes/ResponseToJsontreat anullbuffer as an empty body (emptyTree/ emptybyte[]).
HttpClient.ws(...) returns a WebSocketConnection and auto-rewrites http(s):// URLs to ws(s)://. Behavior lives in WebSocketConnection:
- A built-in heartbeat sends
"!"text frames and detects dead connections, auto-reconnecting on timeout. The scheduler runs atheartbeatInterval / 3. Defaults (WebSocketParams, in seconds):heartbeatInterval=60,heartbeatTimeout=10,reconnectDelay=3. - Incoming text frames are reassembled across fragments;
"!"heartbeat frames are filtered out; JSON payloads (starting{/[) are parsed toTree, other content wrapped in aCheckedTree, then delivered toWebSocketHandler.onMessage. WebSocketHandleris a@FunctionalInterface(onlyonMessageis required;onOpen/onError/onClosehave defaults). State is managed with atomics;connect()/disconnect()return Promises andwaitForConnection(timeout, unit)blocks until open.
- Every source file carries the MIT license header block — preserve it when creating new files.
- Public API methods are heavily Javadoc'd with usage
<pre>examples; match that style for new public methods. - Indentation is tabs.