diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c0c509f003e..82779c42434 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,14 +10,14 @@ jobs:
jdk: ['8']
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.jdk }}
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
- distribution: temurin
+ distribution: liberica
java-version: ${{ matrix.jdk }}
- name: Cache local Ivy repository
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: ~/.ivy/cache
key: ${{ runner.os }}-ivy-${{ hashFiles('**/ivy.xml') }}
@@ -39,7 +39,7 @@ jobs:
run: ./build.sh test -Dtest.haltonerror=true -Dtest.haltonfailure=true
- name: Archive build logs
if: always()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: ${{ runner.os }}-jdk${{ matrix.jdk }}-build-logs
retention-days: 5
diff --git a/build/scripts/junit.xml b/build/scripts/junit.xml
index 67c977efebf..8a0325b5130 100644
--- a/build/scripts/junit.xml
+++ b/build/scripts/junit.xml
@@ -40,6 +40,9 @@
depends="test-prepare, test-local, test-extensions, test-jetty, test-concurrent, test-remote, test-recovery, test-wrapup, test-ant"
description="Run jUnit tests"/>
+
+
+
@@ -157,6 +160,7 @@
+
@@ -261,6 +265,7 @@
+
@@ -299,6 +304,7 @@
+
@@ -328,6 +334,7 @@
+
@@ -388,6 +395,7 @@
+
@@ -424,6 +432,7 @@
+
@@ -453,6 +462,7 @@
+
@@ -522,6 +532,7 @@
+
@@ -551,6 +562,7 @@
+
@@ -591,6 +603,7 @@
+
@@ -608,6 +621,7 @@
+
diff --git a/exist-core/src/main/java/org/exist/plugin/PluginsManagerImpl.java b/exist-core/src/main/java/org/exist/plugin/PluginsManagerImpl.java
index 2aa06560139..f4fdeb1768c 100644
--- a/exist-core/src/main/java/org/exist/plugin/PluginsManagerImpl.java
+++ b/exist-core/src/main/java/org/exist/plugin/PluginsManagerImpl.java
@@ -236,7 +236,7 @@ public void addPlugin(final String className) {
// NOTE: must set interrupted flag
Thread.currentThread().interrupt();
}
- LOG.error(e);
+ LOG.warn(e);
}
}
diff --git a/exist-core/src/main/java/org/exist/storage/recovery/RecoveryManager.java b/exist-core/src/main/java/org/exist/storage/recovery/RecoveryManager.java
index b7d78f9f538..7798a537a16 100644
--- a/exist-core/src/main/java/org/exist/storage/recovery/RecoveryManager.java
+++ b/exist-core/src/main/java/org/exist/storage/recovery/RecoveryManager.java
@@ -45,6 +45,8 @@
import com.evolvedbinary.j8fu.function.SupplierE;
import org.exist.util.sanity.SanityCheck;
+import javax.annotation.Nullable;
+
/**
* Database recovery. This class is used once during startup to check
* if the database is in a consistent state. If not, the class attempts to recover
@@ -59,11 +61,13 @@ public class RecoveryManager {
private final DBBroker broker;
private final JournalRecoveryAccessor journalRecovery;
private final boolean restartOnError;
+ private final boolean hideProgressBar;
public RecoveryManager(final DBBroker broker, final JournalManager journalManager, final boolean restartOnError) {
this.broker = broker;
this.journalRecovery = journalManager.getRecoveryAccessor(this);
this.restartOnError = restartOnError;
+ this.hideProgressBar = Boolean.getBoolean("exist.recovery.progressbar.hide");
}
/**
@@ -121,10 +125,10 @@ public boolean recover() throws LogException {
Lsn lastLsn = Lsn.LSN_INVALID;
Loggable next;
try {
- final ProgressBar progress = new ProgressBar("Scanning journal ", FileUtils.sizeQuietly(last));
+ final long lastSize = FileUtils.sizeQuietly(last);
+ @Nullable final ProgressBar scanProgressBar = hideProgressBar ? null : new ProgressBar("Scanning journal ", lastSize);
while ((next = reader.nextEntry()) != null) {
// LOG.debug(next.dump());
- progress.set(next.getLsn().getOffset());
if (next.getLogType() == LogEntryTypes.TXN_START) {
// new transaction starts: add it to the transactions table
txnsStarted.put(next.getTransactionId(), next);
@@ -136,7 +140,15 @@ public boolean recover() throws LogException {
lastCheckpoint = (Checkpoint) next;
}
lastLsn = next.getLsn();
+
+ if (scanProgressBar != null) {
+ scanProgressBar.set(next.getLsn().getOffset());
+ }
}
+
+ if (scanProgressBar != null) {
+ scanProgressBar.set(lastSize); // 100%
+ }
} catch (final LogException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Caught exception while reading log", e);
@@ -147,7 +159,7 @@ public boolean recover() throws LogException {
// if the last checkpoint record is not the last record in the file
// we need a recovery.
if ((lastCheckpoint == null || !lastCheckpoint.getLsn().equals(lastLsn)) &&
- txnsStarted.size() > 0) {
+ !txnsStarted.isEmpty()) {
LOG.info("Dirty transactions: " + txnsStarted.size());
// starting recovery: reposition the log reader to the last checkpoint
if (lastCheckpoint == null)
@@ -251,10 +263,11 @@ private void doRecovery(final int txnCount, final Path last, final JournalReader
// ------- REDO ---------
if (LOG.isInfoEnabled())
{LOG.info("First pass: redoing " + txnCount + " transactions...");}
- final ProgressBar progress = new ProgressBar("Redo ", FileUtils.sizeQuietly(last));
Loggable next = null;
int redoCnt = 0;
try {
+ final long lastSize = FileUtils.sizeQuietly(last);
+ @Nullable final ProgressBar redoProgressBar = hideProgressBar ? null : new ProgressBar("Redo ", lastSize);
while ((next = reader.nextEntry()) != null) {
SanityCheck.ASSERT(next.getLogType() != LogEntryTypes.CHECKPOINT,
"Found a checkpoint during recovery run! This should not ever happen.");
@@ -272,9 +285,19 @@ private void doRecovery(final int txnCount, final Path last, final JournalReader
// LOG.debug("Redo: " + next.dump());
// redo the log entry
next.redo();
- progress.set(next.getLsn().getOffset());
- if (next.getLsn().equals(lastLsn))
- {break;} // last readable entry reached. Stop here.
+
+ if (redoProgressBar != null) {
+ redoProgressBar.set(next.getLsn().getOffset());
+ }
+
+ if (next.getLsn().equals(lastLsn)) {
+ // last readable entry reached. Stop here.
+ break;
+ }
+ }
+
+ if (redoProgressBar != null) {
+ redoProgressBar.set(lastSize); // 100% done
}
} catch (final Exception e) {
LOG.error("Exception caught while redoing transactions. Aborting recovery to avoid possible damage. " +
@@ -291,16 +314,19 @@ private void doRecovery(final int txnCount, final Path last, final JournalReader
{LOG.info("Second pass: undoing dirty transactions. Uncommitted transactions: " +
runningTxns.size());}
// see if there are uncommitted transactions pending
- if (runningTxns.size() > 0) {
+ if (!runningTxns.isEmpty()) {
// do a reverse scan of the log, undoing all uncommitted transactions
try {
+ final long lastSize = FileUtils.sizeQuietly(last);
+ @Nullable final ProgressBar undoProgressBar = hideProgressBar ? null : new ProgressBar("Undo ", lastSize);
while((next = reader.previousEntry()) != null) {
if (next.getLogType() == LogEntryTypes.TXN_START) {
if (runningTxns.get(next.getTransactionId()) != null) {
runningTxns.remove(next.getTransactionId());
- if (runningTxns.size() == 0)
+ if (runningTxns.isEmpty()) {
// all dirty transactions undone
- {break;}
+ break;
+ }
}
} else if (next.getLogType() == LogEntryTypes.TXN_COMMIT) {
// ignore already committed transaction
@@ -314,6 +340,14 @@ private void doRecovery(final int txnCount, final Path last, final JournalReader
// LOG.debug("Undo: " + next.dump());
next.undo();
}
+
+ if (undoProgressBar != null) {
+ undoProgressBar.set(lastSize - next.getLsn().getOffset());
+ }
+ }
+
+ if (undoProgressBar != null) {
+ undoProgressBar.set(lastSize); // 100% done
}
} catch (final Exception e) {
LOG.warn("Exception caught while undoing dirty transactions. Remaining transactions " +