Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5d99a0e
feat: add license headers and configure JaCoCo for code coverage
peter-lawrey Oct 31, 2025
d7b8f1d
test(sonar): stabilize AppenderFileHandleLeakTest and reduce load und…
peter-lawrey Oct 31, 2025
aaa5026
chore(license): apply license headers via license-maven-plugin format…
peter-lawrey Oct 31, 2025
6a3ca4d
test: skip ChronicleHistoryReaderMainTest on JDK 17+
peter-lawrey Oct 31, 2025
8c7ba0b
test: add CLI coverage and adjust JaCoCo thresholds
peter-lawrey Oct 31, 2025
650195d
docs(api): add @deprecated Javadoc and clean CLI tests
peter-lawrey Oct 31, 2025
c87aec3
test: clarify ChronicleQueueTest stub no-op methods to satisfy Sonar …
peter-lawrey Oct 31, 2025
812fc12
test: add ChronicleReaderMain CLI smoke test
peter-lawrey Oct 31, 2025
987d2f8
build(sonar): ensure JaCoCo agent is injected via \ to fix missing ex…
peter-lawrey Oct 31, 2025
b01e9aa
test(sonar): address code smells — add assertions for ExcerptAppender…
peter-lawrey Oct 31, 2025
cafebd9
tests(reader): add CLI error-path coverage for content-based limiter …
peter-lawrey Oct 31, 2025
8fcaf83
ci(coverage): update JaCoCo thresholds to current measured baseline (…
peter-lawrey Oct 31, 2025
11a67fd
tests(cache): migrate RollingResourcesCache coverage from adv/code-re…
peter-lawrey Oct 31, 2025
b25041f
tests: align ChronicleRollingIssueTest cleanup with adv/code-review; …
peter-lawrey Oct 31, 2025
63fff0d
ci(coverage): raise JaCoCo thresholds to current baseline (lines 0.49…
peter-lawrey Oct 31, 2025
f636aa8
tests: fix S2093 in ChronicleReaderMainTest (try-with-resources) and …
peter-lawrey Oct 31, 2025
37decee
license: normalise headers in tests to pass license:check
peter-lawrey Oct 31, 2025
cda07a4
docs: prefer NIO delete in examples; code: add default cases for robu…
peter-lawrey Oct 31, 2025
d721706
queue: clarify intentional no-op in default method
peter-lawrey Oct 31, 2025
cbb734b
tests: adopt SecurityManager trap for System.exit in ChronicleHistory…
peter-lawrey Oct 31, 2025
d414315
tests: extend RollingResourcesCacheTest with epoch/format round‑trip …
peter-lawrey Oct 31, 2025
8d4856e
build: exclude all .adoc files from license checks
peter-lawrey Oct 31, 2025
d790334
tests: add coverage inspired by demo usage
peter-lawrey Oct 31, 2025
2de4379
tests: add QueueOffsetSpec coverage; headers verified via license:for…
peter-lawrey Oct 31, 2025
dcb261d
build: stop auto-running sortpom; only run when requested
peter-lawrey Oct 31, 2025
92e4f8c
tests: relax PretouchUtilTest to avoid assuming enterprise handler re…
peter-lawrey Oct 31, 2025
c314069
pretoucher: return no-op in OSS fallback to avoid recursion
peter-lawrey Oct 31, 2025
021af1a
tests: add PretoucherCreationTest to ensure OSS fallback is non-recur…
peter-lawrey Oct 31, 2025
75d2779
refactor: rename files for consistency and update license header to A…
peter-lawrey Nov 1, 2025
1f1955b
fix: update JaCoCo properties in pom.xml and correct JSON formatting …
peter-lawrey Nov 1, 2025
c6a1d2f
replace the license header with a one-liner
peter-lawrey Nov 1, 2025
2cac7bf
Merge remote-tracking branch 'origin/develop' into adv/add-jaccoco
peter-lawrey Nov 1, 2025
24d2af6
Merge remote-tracking branch 'origin/develop' into adv/add-jaccoco
peter-lawrey Nov 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/File-size-benchmark.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
====
Copyright 2016-2025 chronicle.software

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
====

Warmed up
messageSize 64
messages 488110620
Expand Down
16 changes: 16 additions & 0 deletions docs/RunLargeQueueMain-report.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
====
Copyright 2016-2025 chronicle.software

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
====

1: Took 1.413 seconds to write and 0.354 seconds to read 1 GB
2: Took 1.253 seconds to write and 0.456 seconds to read 1 GB
3: Took 1.236 seconds to write and 0.246 seconds to read 1 GB
Expand Down
158 changes: 91 additions & 67 deletions pom.xml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,11 @@ default <T> VanillaMethodWriterBuilder<T> methodWriterBuilder(@NotNull final Cla
* The value returned is always a power of two.
*
* @return the Delta Checkpoint Interval for this ChronicleQueue
* @deprecated This configuration is no longer used by the queue implementation and is
* scheduled for removal in release x.29. There is no direct replacement; callers should
* avoid depending on this value and rely on default queue behavior instead.
*/
@Deprecated(/* to be removed in x.29 */)
@Deprecated
default int deltaCheckpointInterval() {
return 64;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,15 @@ private InternalFileUtil() {
*
* <pre>{@code
* for (File file : removableFileCandidates(baseDir).collect(Collectors.toList())) {
* if (!file.delete()) {
* try {
* java.nio.file.Files.delete(file.toPath());
* } catch (java.io.IOException e) {
* // Stop at first failure to preserve order guarantees
* break;
* }
* }
* }</pre>
* }
* </pre>
*
* @param baseDir containing queue file removal candidates
* @return a Stream of roll Queue files that are likely removable
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/net/openhft/chronicle/queue/util/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,15 @@ private FileUtil() { }
*
* <pre>{@code
* for (File file : removableFileCandidates(baseDir).collect(Collectors.toList())) {
* if (!file.delete()) {
* try {
* java.nio.file.Files.delete(file.toPath());
* } catch (java.io.IOException e) {
* // Stop at first failure to preserve order guarantees
* break;
* }
* }
* }</pre>
* }
* </pre>
*
* @param baseDir containing queue file removal candidates
* @return a Stream of roll Queue files that are likely removable
Expand Down
20 changes: 16 additions & 4 deletions src/main/java/net/openhft/chronicle/queue/util/PretouchUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,26 @@ public EventHandler createEventHandler(@NotNull final SingleChronicleQueue queue
}

/**
* Creates a basic {@link Pretoucher} for the specified {@link SingleChronicleQueue}.
* Creates a basic no-op {@link Pretoucher} for OSS builds.
*
* @param queue The {@link SingleChronicleQueue} instance
* @return A basic {@link Pretoucher} instance
* In community (non-enterprise) environments there is no pretoucher implementation;
* returning a no-op avoids recursion via ChronicleQueue#createPretoucher()'s default
* method, which delegates back to PretouchUtil and would otherwise cause a
* StackOverflowError.
*/
@Override
public Pretoucher createPretoucher(@NotNull final SingleChronicleQueue queue) {
return queue.createPretoucher();
return new Pretoucher() {
@Override
public void execute() {
// intentionally no-op in OSS fallback
}

@Override
public void close() {
// intentionally no-op
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*
* Copyright 2016-2025 chronicle.software; SPDX-License-Identifier: Apache-2.0
*/
package net.openhft.chronicle.queue;

import net.openhft.chronicle.queue.reader.ChronicleHistoryReader;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.junit.Test;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.*;

public class ChronicleHistoryReaderMainCliTest extends QueueTestCommon {

@Test
public void runConfiguresReaderFromArguments() throws Exception {
final Path queueDir = Files.createTempDirectory("history-reader");
final TestChronicleHistoryReaderMain main = new TestChronicleHistoryReaderMain();

main.run(new String[]{"-d", queueDir.toString(), "-p", "-m", "-t", "SECONDS", "-i", "2", "-w", "5", "-u", "1"});

RecordingChronicleHistoryReader reader = main.reader;
assertTrue(reader.executeCalled);
assertTrue(reader.closed);
assertEquals(queueDir, reader.basePath());
assertTrue(reader.progress());
assertTrue(reader.histosByMethod());
assertEquals(TimeUnit.SECONDS, reader.timeUnit());
assertEquals(2L, reader.ignoreCount());
assertEquals(TimeUnit.SECONDS.toNanos(5), reader.measurementWindowNanos());
assertEquals(1, reader.summaryOutputOffset());
assertNotNull(reader.messageSink());
}

@Test
public void parseCommandLineWithHelpOption() {
final TestChronicleHistoryReaderMain main = new TestChronicleHistoryReaderMain();

try {
main.parseCommandLine(new String[]{"-h"}, main.options());
fail("Expected HelpExit");
} catch (HelpExit e) {
assertEquals(0, e.status);
assertTrue(main.helpOutput.toString().contains("ChronicleHistoryReaderMain"));
}
}

@Test
public void parseCommandLineMissingDirectoryPrintsError() {
final TestChronicleHistoryReaderMain main = new TestChronicleHistoryReaderMain();

try {
main.parseCommandLine(new String[]{"-t", "SECONDS"}, main.options());
fail("Expected HelpExit");
} catch (HelpExit e) {
assertEquals(1, e.status);
assertTrue(main.helpOutput.toString().contains("Missing required option"));
}
}

private static final class TestChronicleHistoryReaderMain extends ChronicleHistoryReaderMain {
final RecordingChronicleHistoryReader reader = new RecordingChronicleHistoryReader();
final StringBuilder helpOutput = new StringBuilder();

@Override
protected ChronicleHistoryReader chronicleHistoryReader() {
return reader;
}

@Override
protected void printHelpAndExit(Options options, int status, String message) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
new org.apache.commons.cli.HelpFormatter().printHelp(
pw,
180,
this.getClass().getSimpleName(),
message,
options,
org.apache.commons.cli.HelpFormatter.DEFAULT_LEFT_PAD,
org.apache.commons.cli.HelpFormatter.DEFAULT_DESC_PAD,
null,
true
);
pw.flush();
helpOutput.append(sw);
throw new HelpExit(status);
}

@Override
protected CommandLine parseCommandLine(String[] args, Options options) {
return super.parseCommandLine(args, options);
}
}

private static final class RecordingChronicleHistoryReader extends ChronicleHistoryReader {
boolean executeCalled;
boolean closed;

@Override
public ChronicleHistoryReader withMessageSink(java.util.function.Consumer<String> messageSink) {
return super.withMessageSink(messageSink);
}

@Override
public ChronicleHistoryReader withBasePath(Path path) {
return super.withBasePath(path);
}

@Override
public ChronicleHistoryReader withProgress(boolean p) {
return super.withProgress(p);
}

@Override
public ChronicleHistoryReader withTimeUnit(TimeUnit p) {
return super.withTimeUnit(p);
}

@Override
public ChronicleHistoryReader withHistosByMethod(boolean b) {
return super.withHistosByMethod(b);
}

@Override
public ChronicleHistoryReader withIgnore(long ignore) {
return super.withIgnore(ignore);
}

@Override
public ChronicleHistoryReader withMeasurementWindow(long measurementWindow) {
return super.withMeasurementWindow(measurementWindow);
}

@Override
public ChronicleHistoryReader withSummaryOutput(int offset) {
return super.withSummaryOutput(offset);
}

@Override
public void execute() {
executeCalled = true;
}

@Override
public void close() {
closed = true;
}

Path basePath() {
return basePath;
}

boolean progress() {
return progress;
}

boolean histosByMethod() {
return histosByMethod;
}

TimeUnit timeUnit() {
return timeUnit;
}

long ignoreCount() {
return ignore;
}

long measurementWindowNanos() {
return measurementWindowNanos;
}

int summaryOutputOffset() {
return summaryOutputOffset;
}

java.util.function.Consumer<String> messageSink() {
return messageSink;
}
}

private static final class HelpExit extends RuntimeException {
private static final long serialVersionUID = 1L;
final int status;

HelpExit(int status) {
this.status = status;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,25 +131,14 @@ public void testParseCommandLine() {

@Test
public void testParseCommandLineHelpOption() {
ChronicleHistoryReaderMain main = new ChronicleHistoryReaderMain() {
@Override
protected void printHelpAndExit(Options options, int status, String message) {
assertEquals(0, status); // Ensure help is printed with status 0 (success)
throw new ThreadDeath(); // Exit without calling System.exit()
}
};
ChronicleHistoryReaderMain main = new ChronicleHistoryReaderMain();
String[] args = {"-h"};

// Manually setting the security manager to catch System.exit() if needed
try {
main.run(args); // Should trigger the help message and exit with 0
fail("Expected ThreadDeath to be thrown");

} catch (ThreadDeath e) {
// Expected exception

fail("Expected SecurityException due to System.exit(0)");
} catch (SecurityException e) {
fail("System.exit was called unexpectedly.");
assertTrue(e.getMessage().contains("System exit attempted with status: 0"));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2016-2025 chronicle.software; SPDX-License-Identifier: Apache-2.0
*/
package net.openhft.chronicle.queue;

import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder;
import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import static org.junit.Assert.assertTrue;

public class ChronicleReaderMainCliTest extends QueueTestCommon {

@Test
public void mainReadsAndPrintsQueueRecords() {
final java.io.File dir = getTmpDir();
try (ChronicleQueue queue = SingleChronicleQueueBuilder.binary(dir).build();
ExcerptAppender appender = queue.createAppender()) {
appender.writeText("hello");
}

final PrintStream originalOut = System.out;
final ByteArrayOutputStream capture = new ByteArrayOutputStream();
System.setOut(new PrintStream(capture));
try {
ChronicleReaderMain.main(new String[]{"-d", dir.getAbsolutePath()});
} finally {
System.setOut(originalOut);
}

final String out = capture.toString();
assertTrue("Expected output to contain written text", out.contains("hello"));
}

@Test(expected = IllegalArgumentException.class)
public void invalidContentBasedLimiterClassThrows() {
final java.io.File dir = getTmpDir();
ChronicleReaderMain main = new ChronicleReaderMain();
main.run(new String[]{"-d", dir.getAbsolutePath(), "-cbl", "not.a.RealClass"});
}

@Test(expected = ClassNotFoundException.class)
public void invalidBinarySearchComparatorClassThrows() {
final java.io.File dir = getTmpDir();
ChronicleReaderMain main = new ChronicleReaderMain();
main.run(new String[]{"-d", dir.getAbsolutePath(), "-b", "not.a.RealClass"});
}
}
Loading