A dependency-free Java 21 library of small backend building blocks for concurrency, streaming statistics, precise decimal math, and adaptive batch sizing.
The project favors explicit contracts, deterministic behavior, and testable lifecycle rules over framework-specific integrations.
This project is currently a pre-release library. The source code is public and licensed under MIT, but the artifact is not yet published to Maven Central or another public package registry.
To use it today, clone the repository and install the artifact into your local Maven repository. The current coordinates are:
dev.azamir:java-backend-primitives:1.0.0-SNAPSHOT
- Java 21 or later
- Maven 3.9 or later
git clone https://github.com/azamir911/java-backend-primitives.git
cd java-backend-primitives
mvn clean installThis installs the library into your local Maven repository, normally under ~/.m2/repository.
After running mvn clean install, add:
<dependency>
<groupId>dev.azamir</groupId>
<artifactId>java-backend-primitives</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>Use the local Maven repository:
repositories {
mavenLocal()
}
dependencies {
implementation("dev.azamir:java-backend-primitives:1.0.0-SNAPSHOT")
}| Area | Main types | Purpose |
|---|---|---|
| Concurrent queues | CloseableBlockingQueue, BoundedBlockingQueue, PollingBackoffQueue |
Bounded FIFO coordination with explicit shutdown behavior |
| Streaming statistics | StreamingStatistics, StatisticsSnapshot |
One-pass descriptive statistics with thread-safe snapshots |
| Decimal ratios | DecimalRatioCalculator |
Ratio calculations with explicit scale and rounding |
| Adaptive batching | AdaptiveBatchSizer, RetryingSizeProbe |
Estimate safe batch sizes from serialized samples |
| Atomic accumulation | ResettableLongAccumulator |
Lock-free accumulation with atomic snapshot and reset |
import dev.azamir.primitives.concurrent.CloseableBlockingQueue;
import dev.azamir.primitives.concurrent.CloseableQueues;
import dev.azamir.primitives.concurrent.QueueClosedException;
import dev.azamir.primitives.concurrent.QueueWaitStrategy;
CloseableBlockingQueue<String> queue =
CloseableQueues.bounded(128, QueueWaitStrategy.CONDITION_SIGNALING);
Thread producer = Thread.ofVirtual().start(() -> {
try {
queue.put("event-1");
queue.put("event-2");
} catch (InterruptedException interrupted) {
Thread.currentThread().interrupt();
} finally {
queue.close();
}
});
try {
while (true) {
System.out.println(queue.take());
}
} catch (QueueClosedException drained) {
// The queue was closed and all accepted elements were consumed.
}
producer.join();Choose QueueWaitStrategy.POLLING_BACKOFF when polling behavior is intentionally required. The default condition-signaling implementation is more efficient for normal producer-consumer workloads.
| State | put / offer |
take |
poll |
|---|---|---|---|
| Open with capacity | Accepts an element | Returns or waits | Returns or times out |
| Open and full | Waits or times out | Returns an element | Returns an element |
| Closed with buffered data | Throws QueueClosedException |
Drains buffered data | Drains buffered data |
| Closed and drained | Throws QueueClosedException |
Throws QueueClosedException |
Returns empty |
close() is idempotent and preserves buffered elements for draining. closeNow() closes the queue and discards buffered elements. clear() removes buffered elements without closing the queue.
All blocking methods propagate InterruptedException and preserve standard Java interruption semantics.
import dev.azamir.primitives.statistics.StatisticsSnapshot;
import dev.azamir.primitives.statistics.StreamingStatistics;
StreamingStatistics statistics = new StreamingStatistics();
statistics.add(12.5);
statistics.add(18.0);
statistics.add(14.5);
StatisticsSnapshot snapshot = statistics.snapshot();
long count = snapshot.count();
double sum = snapshot.sum();
double mean = snapshot.mean().orElseThrow();
double deviation = snapshot.sampleStandardDeviation().orElseThrow();StreamingStatistics calculates count, sum, mean, sample variance, sample standard deviation, minimum, and maximum in one pass. It uses Welford's algorithm for stable online variance and compensated summation to reduce floating-point error.
Updates, snapshots, and resets are thread-safe.
import dev.azamir.primitives.math.DecimalRatioCalculator;
import java.math.BigDecimal;
import java.math.RoundingMode;
DecimalRatioCalculator ratio =
new DecimalRatioCalculator(new BigDecimal("0.10"));
BigDecimal result = ratio.calculate(
new BigDecimal("2"),
new BigDecimal("3"),
2,
RoundingMode.HALF_UP);The constructor argument is an optional additive adjustment applied after division. Use BigDecimal.ZERO for a plain ratio.
The calculator validates zero denominators and invalid scales rather than relying on implicit arithmetic failures.
import dev.azamir.primitives.batching.AdaptiveBatchSizer;
import dev.azamir.primitives.batching.RetryingSizeProbe;
import java.nio.charset.StandardCharsets;
byte[] serializedSample =
"record-1,record-2".getBytes(StandardCharsets.UTF_8);
int sampledRecords = 2;
int headerBytes = 4;
RetryingSizeProbe<byte[]> probe =
new RetryingSizeProbe<>(bytes -> bytes.length, 3);
AdaptiveBatchSizer sizer =
new AdaptiveBatchSizer(
1_048_576, // target serialized capacity
0.85, // safety factor
1, // minimum batch size
100_000); // maximum batch size
int recordsPerBatch = sizer.estimate(
sampledRecords,
serializedSample,
probe,
headerBytes);The estimator separates fixed overhead from per-record payload, applies a safety factor, and clamps the result to configured minimum and maximum batch sizes.
RetryingSizeProbe<T> allows transient measurement or serialization failures to be retried within a fixed attempt budget while keeping the sizing algorithm independent from any serializer or framework.
import dev.azamir.primitives.concurrent.ResettableLongAccumulator;
ResettableLongAccumulator bytes = ResettableLongAccumulator.sum();
bytes.accumulate(1_024);
bytes.accumulate(2_048);
long intervalTotal = bytes.getAndReset(); // 3072Custom associative operations are also supported. The supplied operation must be side-effect free because it may be retried during concurrent updates.
mvn verifyThe verification phase:
- compiles the library with Java 21
- runs unit tests
- runs multi-threaded stress tests
- creates a JaCoCo report under
target/site/jacoco
Build and run the JMH queue benchmark:
mvn -Pbenchmarks -DskipTests package
java -jar target/benchmarks.jarThe benchmark compares condition signaling and polling backoff. Results depend on the machine, JVM, queue capacity, wait strategy, and producer-consumer ratio. The repository includes a reproducible harness rather than publishing a hardware-independent performance claim.
The current 1.0.0-SNAPSHOT version is a development build. Public APIs may change before the first tagged stable release.
A stable release should use a non-SNAPSHOT version and be published to a public artifact registry before consumers rely on it as a remote dependency.
MIT