Skip to content

Commit c2936df

Browse files
BoxStore: make error output stream customizable by tests
Also prepare to customize the regular output stream, but do not add a builder API until it is required.
1 parent 8278251 commit c2936df

2 files changed

Lines changed: 44 additions & 11 deletions

File tree

objectbox-java/src/main/java/io/objectbox/BoxStore.java

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.Closeable;
2222
import java.io.File;
2323
import java.io.IOException;
24+
import java.io.PrintStream;
2425
import java.net.MalformedURLException;
2526
import java.net.URL;
2627
import java.util.ArrayList;
@@ -238,6 +239,7 @@ public static boolean isSyncServerAvailable() {
238239

239240
native long nativePanicModeRemoveAllObjects(long store, int entityId);
240241

242+
private final PrintStream errorOutputStream;
241243
private final File directory;
242244
private final String canonicalPath;
243245
/** Reference to the native store. Should probably get through {@link #getNativeStore()} instead. */
@@ -283,6 +285,7 @@ public static boolean isSyncServerAvailable() {
283285
relinker = builder.relinker;
284286
NativeLibraryLoader.ensureLoaded();
285287

288+
errorOutputStream = builder.errorOutputStream;
286289
directory = builder.directory;
287290
canonicalPath = getCanonicalPath(directory);
288291
verifyNotAlreadyOpen(canonicalPath);
@@ -613,7 +616,7 @@ public Transaction beginTx() {
613616
// Because write TXs are typically not cached, initialCommitCount is not as relevant than for read TXs.
614617
int initialCommitCount = commitCount;
615618
if (debugTxWrite) {
616-
System.out.println("Begin TX with commit count " + initialCommitCount);
619+
getOutput().println("Begin TX with commit count " + initialCommitCount);
617620
}
618621
long nativeTx = nativeBeginTx(getNativeStore());
619622
if (nativeTx == 0) throw new DbException("Could not create native transaction");
@@ -638,7 +641,7 @@ public Transaction beginReadTx() {
638641
// TODO add multithreaded test for this
639642
int initialCommitCount = commitCount;
640643
if (debugTxRead) {
641-
System.out.println("Begin read TX with commit count " + initialCommitCount);
644+
getOutput().println("Begin read TX with commit count " + initialCommitCount);
642645
}
643646
long nativeTx = nativeBeginReadTx(getNativeStore());
644647
if (nativeTx == 0) throw new DbException("Could not create native read transaction");
@@ -698,7 +701,7 @@ public void close() {
698701
// Give open transactions some time to close (BoxStore.unregisterTransaction() calls notify),
699702
// 1000 ms should be long enough for most small operations and short enough to avoid ANRs on Android.
700703
if (hasActiveTransaction()) {
701-
System.out.println("Briefly waiting for active transactions before closing the Store...");
704+
getOutput().println("Briefly waiting for active transactions before closing the Store...");
702705
try {
703706
// It is fine to hold a lock on BoxStore.this as well as BoxStore.unregisterTransaction()
704707
// only synchronizes on "transactions".
@@ -708,7 +711,7 @@ public void close() {
708711
// If interrupted, continue with releasing native resources
709712
}
710713
if (hasActiveTransaction()) {
711-
System.err.println("Transactions are still active:"
714+
getErrorOutput().println("Transactions are still active:"
712715
+ " ensure that all database operations are finished before closing the Store!");
713716
}
714717
}
@@ -745,16 +748,16 @@ private void checkThreadTermination() {
745748
try {
746749
if (!threadPool.awaitTermination(1, TimeUnit.SECONDS)) {
747750
int activeCount = Thread.activeCount();
748-
System.err.println("Thread pool not terminated in time; printing stack traces...");
751+
getErrorOutput().println("Thread pool not terminated in time; printing stack traces...");
749752
Thread[] threads = new Thread[activeCount + 2];
750753
int count = Thread.enumerate(threads);
751754
for (int i = 0; i < count; i++) {
752-
System.err.println("Thread: " + threads[i].getName());
755+
getErrorOutput().println("Thread: " + threads[i].getName());
753756
Thread.dumpStack();
754757
}
755758
}
756759
} catch (InterruptedException e) {
757-
e.printStackTrace();
760+
e.printStackTrace(getErrorOutput());
758761
}
759762
}
760763

@@ -894,7 +897,7 @@ void txCommitted(Transaction tx, @Nullable int[] entityTypeIdsAffected) {
894897
synchronized (txCommitCountLock) {
895898
commitCount++; // Overflow is OK because we check for equality
896899
if (debugTxWrite) {
897-
System.out.println("TX committed. New commit count: " + commitCount + ", entity types affected: " +
900+
getOutput().println("TX committed. New commit count: " + commitCount + ", entity types affected: " +
898901
(entityTypeIdsAffected != null ? entityTypeIdsAffected.length : 0));
899902
}
900903
}
@@ -1013,10 +1016,10 @@ public <T> T callInReadTxWithRetry(Callable<T> callable, int attempts, int initi
10131016
String diagnose = diagnose();
10141017
String message = attempt + " of " + attempts + " attempts of calling a read TX failed:";
10151018
if (logAndHeal) {
1016-
System.err.println(message);
1019+
getErrorOutput().println(message);
10171020
e.printStackTrace();
1018-
System.err.println(diagnose);
1019-
System.err.flush();
1021+
getErrorOutput().println(diagnose);
1022+
getErrorOutput().flush();
10201023

10211024
System.gc();
10221025
System.runFinalization();
@@ -1337,6 +1340,20 @@ public TxCallback<?> internalFailedReadTxAttemptCallback() {
13371340
return failedReadTxAttemptCallback;
13381341
}
13391342

1343+
/**
1344+
* The output stream to print log messages to. Currently {@link System#out}.
1345+
*/
1346+
private PrintStream getOutput() {
1347+
return System.out;
1348+
}
1349+
1350+
/**
1351+
* The error output stream to print log messages to. This is {@link System#err} by default.
1352+
*/
1353+
private PrintStream getErrorOutput() {
1354+
return errorOutputStream;
1355+
}
1356+
13401357
void setDebugFlags(int debugFlags) {
13411358
nativeSetDebugFlags(getNativeStore(), debugFlags);
13421359
}

objectbox-java/src/main/java/io/objectbox/BoxStoreBuilder.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.io.FileOutputStream;
2626
import java.io.InputStream;
2727
import java.io.OutputStream;
28+
import java.io.PrintStream;
2829
import java.lang.reflect.Method;
2930
import java.util.ArrayList;
3031
import java.util.Arrays;
@@ -67,6 +68,12 @@ public class BoxStoreBuilder {
6768
/** The default maximum size the DB can grow to, which can be overwritten using {@link #maxSizeInKByte}. */
6869
public static final int DEFAULT_MAX_DB_SIZE_KBYTE = 1024 * 1024;
6970

71+
/**
72+
* The error output stream {@link BoxStore} uses for logging. Defaults to {@link System#err}, but can be customized
73+
* for tests.
74+
*/
75+
PrintStream errorOutputStream = System.err;
76+
7077
final byte[] model;
7178

7279
/** BoxStore uses this (not baseDirectory/name) */
@@ -145,6 +152,15 @@ public BoxStoreBuilder(byte[] model) {
145152
this.model = Arrays.copyOf(model, model.length);
146153
}
147154

155+
/**
156+
* For testing: set a custom error output stream {@link BoxStore} uses for logging. Defaults to {@link System#err}.
157+
*/
158+
@Internal
159+
BoxStoreBuilder setErrorOutput(PrintStream err) {
160+
errorOutputStream = err;
161+
return this;
162+
}
163+
148164
/**
149165
* Name of the database, which will be used as a directory for database files.
150166
* You can also specify a base directory for this one using {@link #baseDirectory(File)}.

0 commit comments

Comments
 (0)