diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/datamovement/DataMovementManager.java b/marklogic-client-api/src/main/java/com/marklogic/client/datamovement/DataMovementManager.java index 31152d6ef..f024b55bf 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/datamovement/DataMovementManager.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/datamovement/DataMovementManager.java @@ -7,6 +7,7 @@ import com.marklogic.client.io.marker.ContentHandle; import com.marklogic.client.query.*; +import java.io.Closeable; import java.util.Iterator; /** @@ -33,7 +34,15 @@ * dataMovementManager.release(); *} */ -public interface DataMovementManager { +public interface DataMovementManager extends Closeable { + + /** + * @since 8.1.0 + */ + default void close() { + release(); + } + /** Calls release() on all host-specific DatabaseClient instances (but not on * the DatabaseClient instance used to create this DataMovementManager * instance). diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/AbstractClientTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/AbstractClientTest.java new file mode 100644 index 000000000..020aa21d7 --- /dev/null +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/AbstractClientTest.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. + */ +package com.marklogic.client.test; + +import com.marklogic.client.DatabaseClient; +import com.marklogic.junit5.AbstractMarkLogicTest; + +/** + * Intended to be the base class for all future client API tests, as it properly prepares the database by deleting + * documents from previous test runs that were not created as part of deploying the test app. + */ +public abstract class AbstractClientTest extends AbstractMarkLogicTest { + + @Override + protected final DatabaseClient getDatabaseClient() { + return Common.newServerAdminClient(); + } + + @Override + protected final String getJavascriptForDeletingDocumentsBeforeTestRuns() { + return """ + declareUpdate(); + cts.uris('', [], cts.notQuery(cts.collectionQuery(['test-data', 'temporal-collection']))) + .toArray().forEach(item => xdmp.documentDelete(item)) + """; + } +} diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/IncrementalWriteTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/IncrementalWriteTest.java new file mode 100644 index 000000000..83a81e1c5 --- /dev/null +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/IncrementalWriteTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. + */ +package com.marklogic.client.test.datamovement; + +import com.marklogic.client.DatabaseClient; +import com.marklogic.client.datamovement.DataMovementManager; +import com.marklogic.client.datamovement.WriteBatcher; +import com.marklogic.client.io.DocumentMetadataHandle; +import com.marklogic.client.io.StringHandle; +import com.marklogic.client.test.AbstractClientTest; +import com.marklogic.client.test.Common; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class IncrementalWriteTest extends AbstractClientTest { + + private static final DocumentMetadataHandle METADATA = new DocumentMetadataHandle() + .withCollections("incremental-test") + .withPermission("rest-reader", DocumentMetadataHandle.Capability.READ, DocumentMetadataHandle.Capability.UPDATE); + + @Test + void test() { + AtomicInteger writtenCount = new AtomicInteger(); + + try (DatabaseClient client = Common.newClient()) { + WriteBatcherTemplate template = new WriteBatcherTemplate(client); + + template.runWriteJob(writeBatcher -> writeBatcher + .withThreadCount(1) + .withBatchSize(10) + .onBatchSuccess(batch -> writtenCount.addAndGet(batch.getItems().length)), + + writeBatcher -> { + for (int i = 1; i <= 20; i++) { + String uri = "/incremental/test/doc-" + i + ".xml"; + String content = "" + i + "This is document number " + i + ""; + writeBatcher.add(uri, METADATA, new StringHandle(content)); + } + } + ); + } + + assertEquals(20, writtenCount.get()); + } + + // Experimenting with a template that gets rid of some annoying DMSDK boilerplate. + private record WriteBatcherTemplate(DatabaseClient databaseClient) { + + public void runWriteJob(Consumer writeBatcherConfigurer, Consumer writeBatcherUser) { + try (DataMovementManager dmm = databaseClient.newDataMovementManager()) { + WriteBatcher writeBatcher = dmm.newWriteBatcher(); + writeBatcherConfigurer.accept(writeBatcher); + + dmm.startJob(writeBatcher); + + writeBatcherUser.accept(writeBatcher); + writeBatcher.awaitCompletion(); + + dmm.stopJob(writeBatcher); + } + } + } +} diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/AbstractOpticUpdateTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/AbstractOpticUpdateTest.java index 0afa19e8c..df9682e37 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/AbstractOpticUpdateTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/AbstractOpticUpdateTest.java @@ -18,6 +18,7 @@ import com.marklogic.client.row.RawPlanDefinition; import com.marklogic.client.row.RowManager; import com.marklogic.client.row.RowRecord; +import com.marklogic.client.test.AbstractClientTest; import com.marklogic.client.test.Common; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -32,7 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; -public abstract class AbstractOpticUpdateTest { +public abstract class AbstractOpticUpdateTest extends AbstractClientTest { private final static String XML_PREAMBLE = "\n"; @@ -42,10 +43,6 @@ public abstract class AbstractOpticUpdateTest { @BeforeEach public void setup() { - // Subclasses of this test are expected to only write URIs starting with /acme/ (which is used so that test - // URIs show up near the top when exploring the database in qconsole), so delete all of them before running the - // test to ensure a document doesn't already exist. - Common.deleteUrisWithPattern("/acme/*"); Common.client = Common.newClientBuilder().withUsername("writer-no-default-permissions").build(); rowManager = Common.client.newRowManager().withUpdate(true); op = rowManager.newPlanBuilder();