Skip to content

Client API Reference

Client SDK Architecture

Smart routing, near cache, pipelining, and auto-retry built in.

LoomClientApp Thread
Persistent conn / node
node-1
node-2
node-3

This reference covers the LoomCache Java client SDK. The SDK lives in loom-client and targets Java 17+; the root class is LoomClient.

Generated Java API documentation for the client modules is available at the Javadoc index.

The Java public modifier is not the LoomCache support boundary. Some classes in loom-common are public in bytecode so the client, server, compatibility tests, and tooling can share wire helpers. The public Javadoc route is a curated application-facing subset, not every public bytecode type. Application code should import common-module types only when this page documents them as supported or they are required by a supported client method signature.

Stable common support types include documented exceptions, configuration/TLS types, serializer SPI types, Compact serialization types, SerializablePredicate factory methods for server-filtered listeners and continuous-query cache seeding, OffloadableProcessor only as a marker on explicitly allowlisted entry-processor implementations, ScanResult for public map/set scan results, and ScanPage as a stable cursor/key page value when a supported signature or adapter explicitly uses it. Public client scan APIs return ScanResult. OffloadableProcessor is a scheduling hint for eligible local execution paths, not a guarantee of thread placement or permission to bypass the production processor allowlist.

Other com.loomcache.common.protocol classes are wire codecs, payload records, opcodes, or handshake diagnostics. They may remain public for cross-module compatibility, but they are not application APIs. Do not construct protocol frames, depend on numeric message identifiers, call hidden codec helpers, or implement custom wire predicates. For SerializablePredicate, supported callers use all, keyEquals, valueEquals, keyStartsWith, and valueContains; the nested implementation records, constants, encode(), and decode(byte[]) are hidden wire support.

Maven Central coordinates are available only after a tagged release is published. Use the published LoomCache version that matches your cluster. Contributors validating a source checkout should install that checkout locally before running application or documentation validation.

<dependency>
<groupId>com.loomcache</groupId>
<artifactId>loom-client</artifactId>
<version>${loomcache.version}</version>
</dependency>
implementation("com.loomcache:loom-client:$loomcacheVersion")

Plain client applications need only loom-client. Spring Boot applications can depend on loom-spring-boot for auto-configuration; see Spring Boot integration.

Contributors validating the documentation site from a source checkout can regenerate and copy those Javadocs into the public /api/javadoc/ route with:

Terminal window
cd public-documentation
npm run build

Construct the client through LoomClient.builder():

LoomClient client = LoomClient.builder()
.addSeed("loomcache-1.eu-west.prod.lan:5701")
.addSeed("loomcache-2.eu-west.prod.lan:5701")
.addSeed("loomcache-3.eu-west.prod.lan:5701")
.connectionTimeout(Duration.ofSeconds(5))
.requestTimeout(Duration.ofSeconds(15))
.maxRetries(3)
.retryBaseDelay(Duration.ofMillis(100))
.tlsConfig(TlsConfig.builder()
.enabled(true)
.keyStorePath(Path.of("/etc/loomcache/tls/client.p12"))
.keyStorePassword(System.getenv("LOOMCACHE_TLS_KEY_STORE_PASSWORD"))
.trustStorePath(Path.of("/etc/loomcache/tls/truststore.p12"))
.trustStorePassword(System.getenv("LOOMCACHE_TLS_TRUST_STORE_PASSWORD"))
.requireClientAuth(true)
.revocationCheckingEnabled(true)
.revocationSoftFail(false)
.build())
.credentialsFactory(new StaticCredentialsFactory(ClientCredentials.certificate()))
.nearCacheEnabled(true)
.nearCacheTtl(Duration.ofSeconds(30))
.nearCacheMaxSize(10_000)
.build();
client.connect();

The builder above shows the common production shape. Plaintext TLS-disabled clients and forwarded AUTH roles are non-production conveniences. LoomClient.Builder exposes more than 40 public methods; the additional ones most likely to be needed in production are listed here:

MethodDescription
routingMode(ClientRoutingMode)Controls which cluster member receives each request (ALL_MEMBERS by default).
reconnectMode(ClientReconnectMode)Behavior on connection loss (ON by default).
batchMaxOperations(int)Maximum number of operations per LoomBatch; must be positive and no more than the supported release limit.
nearCacheEvictionPolicy(NearCacheEvictionPolicy)Eviction algorithm for the near cache.
topicPollingConfig(TopicPollingConfig)Configures poll interval, backoff, and max backoff for LoomTopic subscriptions.
credentialsFactory(CredentialsFactory)Plug-in credential provider; mutually exclusive with auth(username, roles).
maxRedirects(int)Maximum leader-redirect hops before the request fails (must be positive).
leaderElectionWaitTimeout(Duration)How long the client waits for a new leader after detecting leader loss (default 15 s).
registerClass(Class<?>, int)Register a POJO class for Kryo serialization with the given type ID.
registerClass(Class<T>, int, Serializer<? super T>)As above, with an explicit Kryo serializer.
registerClass(Class<T>, int, Serializer<? super T>, String fingerprint)As above, with a schema-fingerprint guard for cross-client version safety.
asyncStart(boolean)When true, connect() returns before the cluster handshake completes (incompatible with FailoverLoomClient).
maxInFlightRequests(int)Back-pressure ceiling on concurrent in-flight requests.

POJO types passed to registerClass must use identical registrations (same class, same type ID, same serializer) on every client and every cluster member; mismatches cause deserialization failures.

Factory methodReturns
client.getMap(name)LoomMap<K, V>
client.getReplicatedMap(name)LoomReplicatedMap<K, V> (production-gated compatibility handle)
client.getQueue(name)LoomQueue<E>
client.getSet(name)LoomSet<E>
client.getMultiMap(name)LoomMultiMap<K, V>
client.getList(name)LoomList<E>
client.getPriorityQueue(name)LoomPriorityQueue<E>
client.getRingbuffer(name)LoomRingbuffer<E>
client.getRingbuffer(name, capacity)LoomRingbuffer<E> with explicit capacity
client.getReliableTopic(name)LoomReliableTopic<T>
client.getReliableTopic(name, capacity, TopicOverloadPolicy)LoomReliableTopic<T> with custom capacity and overload policy
client.getTopic(name)LoomTopic<String>
client.getTopic(name, Class<T>)LoomTopic<T> with explicit message type
client.getPNCounter(name)LoomPNCounter
client.getGSet(name)LoomGSet<E>
client.getORSet(name)LoomORSet<E>
client.getLWWRegister(name)LoomLWWRegister<V>
client.getIdGenerator(name)LoomIdGenerator (prefetchCount=1, no prefetch validity)
client.getIdGenerator(name, prefetchCount, prefetchValidityMillis)LoomIdGenerator with prefetch tuning
client.getExecutorService(name)LoomExecutorService (production requires explicit task allowlist)

The table lists public factory methods; production support is governed by the per-feature notes below and the generated Javadocs.

LoomClient also retains lower-level public operation methods such as string-name map, queue, and set calls for compatibility adapters, diagnostics, and advanced integrations that already own serialization and naming. New application code should prefer the typed LoomMap, LoomQueue, LoomSet, and related handles above; lower-level methods are not a broader support promise than the typed handle contract documented here.

LoomMap<K,V> generics are a Java client convenience, not Hazelcast-style implicit object mode. Scalar and binary keys/values use type-tagged wire strings. Registered POJO keys/values use a Kryo object envelope and require identical registrations on clients and members; unregistered POJOs fail before writes are sent.

CategoryMethods
Readget(K), containsKey(K), size(), getAll(Collection<K>), getEntryView(K)
Writeput(K, V), putTransient(K, V), putIfAbsent(K, V), putAll(Map), putAll(Map, idempotencyToken), delete(K)boolean, getAndRemove(K), evict(K), removeAll(Collection<K>), removeAll(LoomQuery), clear()
TTL writesputWithTtl(K, V, Duration), putWithTtl(K, V, long, TimeUnit), plus putWithTTL(...) aliases — returns @Nullable V previous value; a ttl of zero means the entry never expires (like put), not immediate expiry
Atomic CASreplace(K, V, V)boolean, compareAndDelete(K, V)boolean, computeIfAbsent(K, Function<K, V>)
Entry proc.executeOnKey(K, EntryProcessor<K, V, R>), executeOnKeys(Set<K>, EntryProcessor<K, V, R>), executeOnEntries(...); the processor must be a named top-level or static-nested class (lambdas/anonymous/local are rejected); production requires explicit processor allowlist
QuerykeySet(LoomQuery), entrySet(LoomQuery), values(LoomQuery), keyset page helpers, project, count, sum, avg, min, max
ListenersaddEntryListener(MapListener, boolean includeValue)String registrationId; key-scoped and server-predicate overloads; removeEntryListener(String)boolean
Iteratescan(cursor), scan(cursor, pattern), scan(cursor, pattern, count); scanner(), scanner(pattern), scanner(pattern, pageSize)
External storeloadAll(boolean replaceExisting), loadAll(Set<K> keys, boolean replaceExisting) for maps with a clustered, leader-owned MapStore
Per-callget(K, Duration), put(K, V, Duration), delete(K, Duration), containsKey(K, Duration)
AsyncgetAsync, putAsync, putAsyncAndGetPrevious, deleteAsync, containsKeyAsync, sizeAsync
IndexesaddIndex(IndexConfig) throws before sending while SQL index maintenance is disabled; addIndexAsync(IndexConfig) returns a failed CompletableFuture
StatsgetStats()LoomMapStats(remoteFetches, totalOps, avgLatencyMs) (with remoteFetchRatePercent())

putAll(Map, idempotencyToken) accepts a nullable application token; blank tokens are rejected. The token is scoped by the authenticated requester or client endpoint, so a restarted client can reuse it for the same caller. Multi-group batches auto-generate a per-batch token. Partial failure throws PartialPutFailureException.

putTransient(K,V) is a replicated in-memory write that bypasses external MapStore write-through; use it only when the value must remain out of the backing store. loadAll(...) triggers a leader-owned MapStore load, replicates loaded entries through Raft without writing them back to the store, no-ops when no MapStore is configured, and is not supported for partitioned/sharded maps. getEntryView(K) returns the current in-memory value plus metadata and does not trigger read-through loading.

  • offer(E), poll(), poll(Duration), take(), peek(), size(), offerAll(Collection), poll(int count), drain(), drainTo(target[, maxElements]), plus async variants (offerAsync, pollAsync, peekAsync, sizeAsync, offerAllAsync, pollAsync(int), drainAsync, drainToAsync).
  • add, remove, contains, size, clear, scan / scanner, and async variants.

LoomTopic<T>client.getTopic(name) / client.getTopic(name, Class<T>)

Section titled “LoomTopic<T> — client.getTopic(name) / client.getTopic(name, Class<T>)”

LoomTopic uses a server-side polling model. Subscriptions receive messages via a background poll loop.

  • publish(T) — publish a message to all subscribers.
  • subscribe(Consumer<T>)int subscriptionId — register a listener; starts the poll scheduler on the first subscription.
  • subscribe(Consumer<T>, Consumer<TopicSequenceLostException>)int subscriptionId — register a listener with an explicit sequence-loss handler.
  • unsubscribe(int subscriptionId)boolean — remove a subscription; shuts down the poll scheduler when no subscriptions remain.
  • closeSubscriptions() — cancel all active subscriptions.
  • activeSubscriptionCount() — number of currently active subscriptions.

Diagnostic counters:

MethodDescription
getDecodeFailureCount()Number of messages that could not be deserialized.
getListenerFailureCount()Number of listener invocations that threw an exception.
getPermanentListenerFailureCount()Messages skipped after listener retry exhaustion; the subscription remains active and advances past the failed sequence.
getPollFailureCount()Number of failed poll requests to the server.
getSequenceLossCount()Number of sequence-gap events detected (missed messages).
getLastSequenceLoss()@Nullable TopicSequenceLostExceptionMost recent sequence-loss event, or null.
getCurrentPollBackoffMs()Current poll backoff in milliseconds (increases after consecutive failures).

LoomTransaction implements AutoCloseable. Closing without committing triggers rollback().

try (LoomTransaction tx = client.newTransaction()) {
tx.put("users", "alice", "Alice Loom");
tx.delete("sessions", "expired-key");
tx.commit();
}
// With an explicit timeout
try (LoomTransaction tx = client.newTransaction(Duration.ofSeconds(10))) {
tx.put("orders", orderId, payload);
tx.commit();
}

Supported transaction operations:

MethodDescription
put(mapName, key, value)Buffer a map put.
delete(mapName, key)Buffer a map delete.
putIfAbsent(mapName, key, value)Buffer a conditional put (applied only if key is absent at commit time).
setAdd(setName, member)Buffer a set add.
setRemove(setName, member)Buffer a set remove.
queueOffer(queueName, element)Buffer a queue offer.
getForUpdate(mapName, key)@Nullable StringRead a key under a server-side transaction lock and open the backing transaction session.
commit()Commit the transaction; throws on conflict or timeout.
rollback()Roll back the transaction (no-op if already committed).

client.newTransaction() uses the default transaction timeout. client.newTransaction(Duration) sets an explicit timeout (minimum 1 ms). A negative duration throws LoomException; passing null uses the default timeout. Cross-group replicated transactions currently support map put and delete only. putIfAbsent, set, and queue transaction operations are limited to the single-group path.

CP primitives — client.consistencySubsystem()

Section titled “CP primitives — client.consistencySubsystem()”

client.consistencySubsystem() returns a LoomConsistencySubsystem, which exposes the production-supported CP data structures:

LoomConsistencySubsystem cp = client.consistencySubsystem();
LoomAtomicLong counter = cp.getAtomicLong("hits");
counter.incrementAndGet();
counter.compareAndSet(10, 20);
LoomLinearizableLock lock = cp.getLock("resource"); // client manages the production CP session internally
LoomLinearizableSemaphore sem = cp.getSemaphore("permits");
  • LoomAtomicLong — fully Raft-replicated get, set, arithmetic, and compare-and-set operations.
  • LoomLinearizableLock — Raft-replicated lock acquire/release. With loomcache.profile=production the server-side apply handler accepts lock mutations only when the caller holds an active Raft-managed CP session; the Java client creates, heartbeats, and closes that session internally.
  • LoomLinearizableSemaphore — Raft-replicated permit acquire/release. The Java client attaches its managed CP session for acquire/release. A sessionless semaphore mutation fails closed in production; available-permit reads are always allowed.
  • Other CP extension methods are not production-supported; production profiles reject their direct requests. Use the embedded ConsistencySubsystem only for non-production embedded scenarios.
client.batch()
.map("users").put("alice", "Alice")
.map("users").put("bob", "Bob")
.map("users").delete("eve")
.execute();

Operations are scoped through .map(name), .set(name), or .queue(name). By default the batch is not atomic. In sharded mode execute() uses the current partition table to pre-split non-atomic keyed map put, delete, and putIfAbsent operations by owner group. Call .atomic() for all-or-nothing handling: single-owner batches run under the server’s mutation lock, and cross-owner-group sharded atomic batches currently support keyed map put and delete through server 2PC. Cross-owner atomic batches containing putIfAbsent, set, or queue operations fail closed because condition-bearing 2PC is disabled. .replicated() implies atomic. Non-splittable mixed-owner batches fail closed. Set/queue batches are supported only on the non-sharded/single-Raft-group path; in sharded mode any batch containing set or queue operations fails closed.

To capture partial results from owner-group splitting, use executeAndReport(), which returns a LoomBatch.Result:

LoomBatch.Result result = client.batch()
.map("a").put("k1", "v1")
.map("b").put("k2", "v2")
.executeAndReport();
if (!result.isFullyCommitted()) {
int committed = result.committedSubBatches();
int total = result.totalSubBatches();
result.failure().ifPresent(ex -> log.error("partial failure after {}/{}", committed, total, ex));
}

LoomBatch.Result fields: committedSubBatches(), totalSubBatches(), failure()Optional<RuntimeException>, isFullyCommitted()boolean.

Run SQL through client.sql(String) and receive a LoomSqlResult. The client routes the request with the _sql_auto sentinel; the server parses the FROM clause and extracts the target map:

// User must have an explicit supported serializer/encoding before production use.
LoomSqlResult result = client.sql("SELECT key, name, age FROM users WHERE age > 30");

See SQL engine for supported syntax. CREATE INDEX <name> ON <mapping> (<cols>) TYPE SORTED|HASH|BITMAP is recognized by the parser but rejected at runtime in this release; LoomMap.addIndex(IndexConfig) throws before sending a request, while LoomMap.addIndexAsync(IndexConfig) completes the returned CompletableFuture exceptionally.

Each data-structure class exposes async-returning methods directly on the class — for example LoomMap.getAsync(K), LoomQueue.offerAsync(E), and LoomMap.executeOnKey(K, EntryProcessor). These methods are the public async API. In production, LoomMap.executeOnKey still requires the explicit deny-all-default entry-processor allowlist.

LoomClient.async() returns an AsyncLoomClient for integration code that needs the lower-level async facade; method names like mapPut(String, String, String) operate on untyped data-structure names and serialized values. Application code should prefer the typed *Async methods on data-structure objects.

  • registerMapListener(String mapName, MapChangeListener) / deregisterMapListener(...) — raw map change events.

LoomClient supports cluster-lifecycle listeners registered before or after connect():

  • addLifecycleListener(LifecycleListener) — notified on STARTING, STARTED, SHUTTING_DOWN, SHUTDOWN, CLIENT_CONNECTED, CLIENT_DISCONNECTED, and CLIENT_CHANGED_CLUSTER (the last emitted by FailoverLoomClient when it transparently switches to a different seed group).
  • addMembershipListener(MembershipListener) — notified when cluster members join or leave.
  • addDistributedObjectListener(DistributedObjectListener) — notified when data structures are created or destroyed on the cluster.
  • FailoverLoomClient — wraps multiple LoomClient instances; on connection loss it transparently retries against the next seed group. Constructed via FailoverLoomClient.connect(ClientFailoverConfig). Incompatible with asyncStart=true.
  • LoomQueryCache<K, V> — a client-side materialized view of a server map; implements AutoCloseable. Located in com.loomcache.client.query.
  • Pipelining — sends a list of operations without waiting for individual responses, then collects results. Reduces round-trip overhead for bulk independent reads.
  • ConnectionPoolMetrics — diagnostics counters and MetricsSnapshot values used by client telemetry. Supported application use is read-only observation when surfaced by diagnostics; callers should not instantiate it or mutate counters as an application-level connection-pool control surface.

Under com.loomcache.client.cache:

  • NearCache — sized LRU with TTL, updated by server-push invalidations.
  • ReadThroughCache, AsyncReadThroughCache — cache-loader fronted with single-flight coalescing.
  • WriteBehindCache — in-process buffer with asynchronous flush to the cluster.
  • CacheLoader, AsyncCacheLoader — SPIs you implement.
  • RetryPolicy — exponential backoff with jitter for known-safe retryable failures. Unknown operation outcomes are not automatically retried; reconcile state or retry only with application-level idempotency or an explicit reusable token where an API provides one. Explicit application tokens are scoped by the authenticated requester or client endpoint, not by the random LoomClient process instance.
  • Smart routing — partition-aware retries with leader fallback.
  • Client-side leader cache — caches the last known leader; cleared explicitly or on authenticated redirect.
  • Router-level mutation idempotency keys protect the client’s own retry loop; a fresh application call gets a fresh key unless the API exposes an explicit reusable application token.
  • The version handshake is fail-closed: peers must use the same artifact version (2.1.0), this build’s supported protocol value, and the same Kryo registration digest. The server rejects unknown negotiated capabilities; the client exposes only recognized capability flags. Handshake payloads reject unknown extension TLVs instead of skipping them. This release has no mixed-version rolling window.
  • Protocol error responses can carry typed payloads or legacy/free-form UTF-8 details. The client maps known typed/error-token payloads to specific LoomException subclasses and otherwise raises LoomException with the server detail. Supported retry handling should catch LoomException and inspect retryClassification() / errorCode(); concrete transport backpressure subclass names are not the supported catch surface.

LoomCache is an independent open-source project. It is not affiliated with, endorsed by, or sponsored by Hazelcast, Inc. or by any other company whose products are named in this documentation. “Hazelcast” is a trademark of Hazelcast, Inc.; references to it are nominative and describe only migration and comparison. All other product and company names are trademarks of their respective owners and are used for identification purposes only.