This is an automated email from the ASF dual-hosted git repository.
OpenPj pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/manifoldcf.git
The following commit(s) were added to refs/heads/trunk by this push:
new 663645e17 Upgrade MongoDB and GridFS connectors to MongoDB Java sync
driver 5.x (CONNECTORS-1791)
663645e17 is described below
commit 663645e1799dbee0de79d45b728f617dae5e2f9b
Author: Piergiorgio Lucidi <[email protected]>
AuthorDate: Tue Oct 6 13:29:01 2026 +0200
Upgrade MongoDB and GridFS connectors to MongoDB Java sync driver 5.x
(CONNECTORS-1791)
---
.github/workflows/ci.yml | 9 +-
build.xml | 28 +-
connectors/gridfs/build.xml | 70 ++++-
.../crawler/connectors/gridfs/GridFSConstants.java | 8 +
.../gridfs/GridFSRepositoryConnector.java | 325 +++++++++++++--------
.../crawler/connectors/gridfs/Messages.java | 8 +-
.../tests/GridFSRepositoryConnectorHSQLDBIT.java | 318 ++++++++++++++++++++
connectors/gridfs/pom.xml | 41 ++-
.../{mongodb => gridfs}/test-materials/README.txt | 5 +-
connectors/mongodb/build.xml | 275 ++++++++---------
.../mongodboutput/MongodbOutputConnector.java | 183 +++++-------
.../mongodboutput/tests/APISanityHSQLDBIT.java | 63 ++--
.../output/mongodboutput/tests/BaseITHSQLDB.java | 39 +--
.../mongodboutput/tests/BaseITSanityTestUtils.java | 4 +-
connectors/mongodb/pom.xml | 9 +-
connectors/mongodb/test-materials/README.txt | 5 +-
16 files changed, 915 insertions(+), 475 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index aee4a5642..7be2134d3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,8 +20,8 @@ run-name: ${{ github.actor }} is executing CI build and tests
🚀
on:
push:
- branches:
- - trunk
+ tags:
+ - '*'
pull_request:
branches:
- trunk
@@ -43,11 +43,6 @@ jobs:
distribution: 'temurin'
architecture: x64
cache: maven
-
- - name: Install OpenSSL 1.1 runtime for embedded legacy MongoDB
- run: |
- sudo apt-get update
- sudo apt-get install -y libssl1.1
- name: Ant Build and Test
run: ant make-core-deps make-deps build test
diff --git a/build.xml b/build.xml
index e46b33c7a..27e2605fb 100644
--- a/build.xml
+++ b/build.xml
@@ -35,7 +35,7 @@
<property name="zookeeper.version" value="3.9.1"/>
<property name="metrics-core.version" value="4.2.21"/>
<property name="snappy-java.version" value="1.1.7.7"/>
- <property name="mongodb.version" value="2.14.3"/>
+ <property name="mongodb-driver-sync.version" value="5.6.5"/>
<property name="postgresql.version" value="42.7.4"/>
<property name="axis.version" value="1.4"/>
<property name="saaj-api.version" value="1.3"/>
@@ -1010,11 +1010,33 @@ Use Apache Forrest version forrest-0.9-dev or higher.
<target name="download-mongo-java-driver">
<mkdir dir="lib"/>
+ <!-- Modern MongoDB sync driver (MongoDB 9.x compliant), used by the
MongoDB output and GridFS repository connectors -->
<antcall target="download-via-maven">
<param name="project-path" value="org/mongodb"/>
- <param name="artifact-version" value="${mongodb.version}"/>
+ <param name="artifact-version"
value="${mongodb-driver-sync.version}"/>
<param name="target" value="lib"/>
- <param name="artifact-name" value="mongo-java-driver"/>
+ <param name="artifact-name" value="mongodb-driver-sync"/>
+ <param name="artifact-type" value="jar"/>
+ </antcall>
+ <antcall target="download-via-maven">
+ <param name="project-path" value="org/mongodb"/>
+ <param name="artifact-version"
value="${mongodb-driver-sync.version}"/>
+ <param name="target" value="lib"/>
+ <param name="artifact-name" value="mongodb-driver-core"/>
+ <param name="artifact-type" value="jar"/>
+ </antcall>
+ <antcall target="download-via-maven">
+ <param name="project-path" value="org/mongodb"/>
+ <param name="artifact-version"
value="${mongodb-driver-sync.version}"/>
+ <param name="target" value="lib"/>
+ <param name="artifact-name" value="bson"/>
+ <param name="artifact-type" value="jar"/>
+ </antcall>
+ <antcall target="download-via-maven">
+ <param name="project-path" value="org/mongodb"/>
+ <param name="artifact-version"
value="${mongodb-driver-sync.version}"/>
+ <param name="target" value="lib"/>
+ <param name="artifact-name" value="bson-record-codec"/>
<param name="artifact-type" value="jar"/>
</antcall>
</target>
diff --git a/connectors/gridfs/build.xml b/connectors/gridfs/build.xml
index 02941f054..a231c1e0a 100644
--- a/connectors/gridfs/build.xml
+++ b/connectors/gridfs/build.xml
@@ -30,10 +30,23 @@
<import file="${mcf-dist}/connector-build.xml"/>
+ <target name="download-cleanup">
+ <delete>
+ <fileset dir="test-materials" excludes="README*.txt"
erroronmissingdir="false"/>
+ </delete>
+ </target>
+
+ <!-- MongoDB sync driver (MongoDB 9.x compliant) -->
+ <patternset id="mongodb-driver-jars">
+ <include name="mongodb-driver-sync-*.jar"/>
+ <include name="mongodb-driver-core-*.jar"/>
+ <include name="bson-*.jar"/>
+ </patternset>
+
<path id="connector-classpath">
<path refid="mcf-connector-build.connector-classpath"/>
<fileset dir="../../lib">
- <include name="mongo-java-driver*.jar"/>
+ <patternset refid="mongodb-driver-jars"/>
</fileset>
</path>
@@ -41,7 +54,7 @@
<mkdir dir="dist/lib"/>
<copy todir="dist/lib">
<fileset dir="../../lib">
- <include name="mongo-java-driver*.jar"/>
+ <patternset refid="mongodb-driver-jars"/>
</fileset>
</copy>
</target>
@@ -52,4 +65,57 @@
<param name="connector-class"
value="org.apache.manifoldcf.crawler.connectors.gridfs.GridFSRepositoryConnector"/>
</antcall>
</target>
+
+ <!-- Test materials: Testcontainers is used to run MongoDB 9.0.2 as a
Docker container during the tests -->
+ <property name="testcontainers.version" value="2.0.5"/>
+ <property name="docker-java.version" value="3.7.1"/>
+ <property name="duct-tape.version" value="1.0.8"/>
+ <property name="jna.version" value="5.10.0"/>
+ <property name="commons-compress.version" value="1.28.0"/>
+ <property name="jetbrains-annotations.version" value="17.0.0"/>
+ <property name="jackson-annotations.version" value="2.21"/>
+
+ <!-- Test materials come first so that the newer jars required by
Testcontainers
+ (e.g. jackson-annotations, commons-compress) take precedence over the
older shared ones -->
+ <path id="connector-test-classpath">
+ <fileset dir="test-materials" includes="*.jar"
erroronmissingdir="false"/>
+ <path refid="mcf-connector-build.connector-test-classpath"/>
+ </path>
+
+ <target name="calculate-testcode-condition">
+ <condition property="tests-present">
+ <and>
+ <available
file="test-materials/testcontainers-${testcontainers.version}.jar"/>
+ <available
file="test-materials/testcontainers-mongodb-${testcontainers.version}.jar"/>
+ <available
file="test-materials/docker-java-api-${docker-java.version}.jar"/>
+ <available
file="test-materials/docker-java-transport-${docker-java.version}.jar"/>
+ <available
file="test-materials/docker-java-transport-zerodep-${docker-java.version}.jar"/>
+ <available
file="test-materials/duct-tape-${duct-tape.version}.jar"/>
+ <available file="test-materials/jna-${jna.version}.jar"/>
+ <available
file="test-materials/commons-compress-${commons-compress.version}.jar"/>
+ <available
file="test-materials/annotations-${jetbrains-annotations.version}.jar"/>
+ <available
file="test-materials/jackson-annotations-${jackson-annotations.version}.jar"/>
+ </and>
+ </condition>
+ </target>
+
+ <target name="pretest-warn" depends="calculate-testcode-condition"
unless="tests-present">
+ <echo message="Need Testcontainers download in order to run tests -
run ant make-deps (Docker is required to run MongoDB 9.0.2)"/>
+ </target>
+
+ <target name="download-dependencies">
+ <property name="maven-repo" value="https://repo1.maven.org/maven2"/>
+ <mkdir dir="test-materials"/>
+ <get
src="${maven-repo}/org/testcontainers/testcontainers/${testcontainers.version}/testcontainers-${testcontainers.version}.jar"
dest="test-materials/testcontainers-${testcontainers.version}.jar"/>
+ <get
src="${maven-repo}/org/testcontainers/testcontainers-mongodb/${testcontainers.version}/testcontainers-mongodb-${testcontainers.version}.jar"
dest="test-materials/testcontainers-mongodb-${testcontainers.version}.jar"/>
+ <get
src="${maven-repo}/com/github/docker-java/docker-java-api/${docker-java.version}/docker-java-api-${docker-java.version}.jar"
dest="test-materials/docker-java-api-${docker-java.version}.jar"/>
+ <get
src="${maven-repo}/com/github/docker-java/docker-java-transport/${docker-java.version}/docker-java-transport-${docker-java.version}.jar"
dest="test-materials/docker-java-transport-${docker-java.version}.jar"/>
+ <get
src="${maven-repo}/com/github/docker-java/docker-java-transport-zerodep/${docker-java.version}/docker-java-transport-zerodep-${docker-java.version}.jar"
dest="test-materials/docker-java-transport-zerodep-${docker-java.version}.jar"/>
+ <get
src="${maven-repo}/org/rnorth/duct-tape/duct-tape/${duct-tape.version}/duct-tape-${duct-tape.version}.jar"
dest="test-materials/duct-tape-${duct-tape.version}.jar"/>
+ <get
src="${maven-repo}/net/java/dev/jna/jna/${jna.version}/jna-${jna.version}.jar"
dest="test-materials/jna-${jna.version}.jar"/>
+ <get
src="${maven-repo}/org/apache/commons/commons-compress/${commons-compress.version}/commons-compress-${commons-compress.version}.jar"
dest="test-materials/commons-compress-${commons-compress.version}.jar"/>
+ <get
src="${maven-repo}/org/jetbrains/annotations/${jetbrains-annotations.version}/annotations-${jetbrains-annotations.version}.jar"
dest="test-materials/annotations-${jetbrains-annotations.version}.jar"/>
+ <get
src="${maven-repo}/com/fasterxml/jackson/core/jackson-annotations/${jackson-annotations.version}/jackson-annotations-${jackson-annotations.version}.jar"
dest="test-materials/jackson-annotations-${jackson-annotations.version}.jar"/>
+ </target>
+
</project>
diff --git
a/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/GridFSConstants.java
b/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/GridFSConstants.java
index 70576c34d..92fd2cf89 100644
---
a/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/GridFSConstants.java
+++
b/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/GridFSConstants.java
@@ -81,6 +81,14 @@ public class GridFSConstants {
* MongoDB default unique key field name.
*/
protected static final String DEFAULT_ID_FIELD_NAME = "_id";
+ /**
+ * GridFS files document metadata field name.
+ */
+ protected static final String METADATA_FIELD_NAME = "metadata";
+ /**
+ * GridFS content type field name (top-level for legacy drivers, inside
metadata for modern ones).
+ */
+ protected static final String CONTENT_TYPE_FIELD_NAME = "contentType";
/**
* Job start point node type.
*/
diff --git
a/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/GridFSRepositoryConnector.java
b/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/GridFSRepositoryConnector.java
index 9a4afb472..b28a25743 100644
---
a/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/GridFSRepositoryConnector.java
+++
b/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/GridFSRepositoryConnector.java
@@ -15,26 +15,31 @@
*/
package org.apache.manifoldcf.crawler.connectors.gridfs;
-import com.mongodb.DB;
-import com.mongodb.DBCollection;
-import com.mongodb.DBCursor;
-import com.mongodb.DBObject;
-import com.mongodb.DBTCPConnector;
-import com.mongodb.Mongo;
-import com.mongodb.MongoClient;
-import com.mongodb.gridfs.GridFS;
-import com.mongodb.gridfs.GridFSDBFile;
+import com.mongodb.MongoClientSettings;
+import com.mongodb.MongoCredential;
+import com.mongodb.MongoException;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoCursor;
+import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.gridfs.GridFSBucket;
+import com.mongodb.client.gridfs.GridFSBuckets;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.Projections;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.InputStream;
-import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import org.apache.commons.lang.StringUtils;
+
+import org.apache.commons.lang3.StringUtils;
import org.apache.manifoldcf.agents.interfaces.RepositoryDocument;
import org.apache.manifoldcf.agents.interfaces.ServiceInterruption;
import org.apache.manifoldcf.core.interfaces.ConfigParams;
@@ -49,6 +54,8 @@ import
org.apache.manifoldcf.crawler.interfaces.IProcessActivity;
import org.apache.manifoldcf.crawler.interfaces.ISeedingActivity;
import org.apache.manifoldcf.crawler.interfaces.IExistingVersions;
import org.apache.manifoldcf.crawler.system.Logging;
+import org.bson.BsonValue;
+import org.bson.Document;
import org.bson.types.ObjectId;
/**
@@ -106,9 +113,13 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
*/
protected String denyAcl = null;
/**
- * MongoDB session.
+ * MongoDB client (owns the connection pool).
+ */
+ protected MongoClient client = null;
+ /**
+ * MongoDB session (database handle).
*/
- protected DB session = null;
+ protected MongoDatabase session = null;
/**
* Last session fetch time.
*/
@@ -214,20 +225,18 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
try {
getSession();
if (session != null) {
- Mongo currentMongoSession = session.getMongo();
- currentMongoSession.getConnector()
- .getDBPortPool(currentMongoSession.getAddress())
- .get()
- .ensureOpen();
- session.getMongo().close();
- session = null;
+ // The modern driver connects and authenticates lazily: a ping
forces a round trip
+ session.runCommand(new Document("ping", 1));
+ closeSession();
return super.check();
}
return "Not connected.";
} catch (ManifoldCFException e) {
+ closeSession();
return e.getMessage();
- } catch (IOException ex) {
- return ex.getMessage();
+ } catch (MongoException ex) {
+ closeSession();
+ return "GridFS: Connection check failed: " + ex.getMessage();
}
}
@@ -241,13 +250,11 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
public void disconnect() throws ManifoldCFException {
if (session != null) {
try {
- session.getMongo().close();
+ closeSession();
} catch (Exception e) {
Logging.connectors.error("GridFS: Error when trying to
disconnect: " + e.getMessage());
throw new ManifoldCFException("GridFS: Error when trying to
disconnect: " + e.getMessage(), e);
}
- session = null;
- lastSessionFetch = -1L;
username = null;
password = null;
host = null;
@@ -258,6 +265,7 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
acl = null;
denyAcl = null;
}
+ super.disconnect();
}
/**
@@ -274,11 +282,7 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
long currentTime = System.currentTimeMillis();
if (currentTime >= lastSessionFetch + SESSION_EXPIRATION_MILLISECONDS)
{
- if (session != null) {
- session.getMongo().close();
- session = null;
- }
- lastSessionFetch = -1L;
+ closeSession();
}
}
@@ -290,12 +294,25 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
*/
@Override
public boolean isConnected() {
- if (session == null) {
- return false;
+ // The client manages its own connection pool (reconnecting as
needed), so an open
+ // client is considered connected; no network round trip is done here.
+ return client != null && session != null;
+ }
+
+ /**
+ * Close the MongoDB client (and its connection pool), if any.
+ */
+ protected void closeSession() {
+ if (client != null) {
+ try {
+ client.close();
+ } catch (Exception e) {
+ Logging.connectors.warn("GridFS: Error closing MongoDB client:
" + e.getMessage(), e);
+ }
}
- Mongo currentMongoSession = session.getMongo();
- DBTCPConnector currentTCPConnection =
currentMongoSession.getConnector();
- return currentTCPConnection.isOpen();
+ client = null;
+ session = null;
+ lastSessionFetch = -1L;
}
/**
@@ -351,17 +368,20 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
String lastSeedVersion, long seedTime, int jobMode)
throws ManifoldCFException, ServiceInterruption {
getSession();
- DBCollection fsFiles = session.getCollection(
+ MongoCollection<Document> fsFiles = session.getCollection(
bucket + GridFSConstants.COLLECTION_SEPERATOR +
GridFSConstants.FILES_COLLECTION_NAME
);
- DBCursor dnc = fsFiles.find();
- while (dnc.hasNext()) {
- DBObject dbo = dnc.next();
- String _id = dbo.get("_id").toString();
- activities.addSeedDocument(_id);
- if (Logging.connectors.isDebugEnabled()) {
- Logging.connectors.debug("GridFS: Document _id = " + _id + "
added to queue");
+ try (MongoCursor<Document> dnc =
fsFiles.find().projection(Projections.include("_id")).iterator()) {
+ while (dnc.hasNext()) {
+ Document dbo = dnc.next();
+ String _id = dbo.get("_id").toString();
+ activities.addSeedDocument(_id);
+ if (Logging.connectors.isDebugEnabled()) {
+ Logging.connectors.debug("GridFS: Document _id = " + _id +
" added to queue");
+ }
}
+ } catch (MongoException e) {
+ handleMongoException(e);
}
return "";
}
@@ -387,22 +407,34 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
for (String documentIdentifier : documentIdentifiers) {
String versionString;
- GridFS gfs;
- GridFSDBFile document;
+ Document document;
+ Document metadata;
getSession();
String _id = documentIdentifier;
- gfs = new GridFS(session, bucket);
- document = gfs.findOne(new ObjectId(_id));
+ // Identifiers are normally ObjectIds, but GridFS allows any _id
type: fall back to the raw string
+ Object idValue = ObjectId.isValid(_id) ? new ObjectId(_id) : _id;
+ GridFSBucket gfs = GridFSBuckets.create(session, bucket);
+ MongoCollection<Document> fsFiles = session.getCollection(
+ bucket + GridFSConstants.COLLECTION_SEPERATOR +
GridFSConstants.FILES_COLLECTION_NAME
+ );
+ try {
+ // Read the raw files document so that legacy top-level fields
(e.g. contentType) are preserved
+ document = fsFiles.find(Filters.eq("_id", idValue)).first();
+ } catch (MongoException e) {
+ handleMongoException(e);
+ return;
+ }
if (document == null) {
activities.deleteDocument(documentIdentifier);
continue;
- } else {
- DBObject metadata = document.getMetaData();
- versionString = document.getMD5() + "+" + metadata != null
- ? Integer.toString(metadata.hashCode())
- : StringUtils.EMPTY;
}
+ metadata = document.get(GridFSConstants.METADATA_FIELD_NAME,
Document.class);
+ long fileLenght = getFileLength(document);
+ Date createdDate = document.getDate("uploadDate");
+ versionString = fileLenght + "+"
+ + (createdDate == null ? "" :
Long.toString(createdDate.getTime())) + "+"
+ + (metadata == null ? "" :
Integer.toString(metadata.toJson().hashCode()));
if (versionString.length() == 0 ||
activities.checkDocumentNeedsReindexing(documentIdentifier, versionString)) {
long startTime = System.currentTimeMillis();
@@ -415,7 +447,6 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
Logging.connectors.debug("GridFS: Processing document
_id = " + _id);
}
- DBObject metadata = document.getMetaData();
if (metadata == null) {
errorCode = "NULLMETADATA";
errorDesc = "Excluded because document had a null
Metadata";
@@ -424,9 +455,9 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
continue;
}
- String urlValue = document.getMetaData().get(this.url) ==
null
+ String urlValue = metadata.get(this.url) == null
? StringUtils.EMPTY
- : document.getMetaData().get(this.url).toString();
+ : metadata.get(this.url).toString();
if (!StringUtils.isEmpty(urlValue)) {
boolean validURL;
try {
@@ -436,14 +467,12 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
validURL = false;
}
if (validURL) {
- long fileLenght = document.getLength();
- Date createdDate = document.getUploadDate();
- String fileName = document.getFilename();
- String mimeType = document.getContentType();
+ String fileName = document.getString("filename");
+ String mimeType = getContentType(document,
metadata);
if (!activities.checkURLIndexable(urlValue)) {
Logging.connectors.warn("GridFS: Document " +
_id + " has a URL excluded by the output connector ('" + urlValue + "') -
skipping.");
- errorCode = activities.EXCLUDED_URL;
+ errorCode = IProcessActivity.EXCLUDED_URL;
errorDesc = "Excluded because of URL (" +
urlValue + ")";
activities.noDocument(_id, version);
continue;
@@ -451,7 +480,7 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
if (!activities.checkLengthIndexable(fileLenght)) {
Logging.connectors.warn("GridFS: Document " +
_id + " has a length excluded by the output connector (" + fileLenght + ") -
skipping.");
- errorCode = activities.EXCLUDED_LENGTH;
+ errorCode = IProcessActivity.EXCLUDED_LENGTH;
errorDesc = "Excluded because of length (" +
fileLenght + ")";
activities.noDocument(_id, version);
continue;
@@ -459,7 +488,7 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
if (!activities.checkMimeTypeIndexable(mimeType)) {
Logging.connectors.warn("GridFS: Document " +
_id + " has a mime type excluded by the output connector ('" + mimeType + "') -
skipping.");
- errorCode = activities.EXCLUDED_MIMETYPE;
+ errorCode = IProcessActivity.EXCLUDED_MIMETYPE;
errorDesc = "Excluded because of mime type ("
+ mimeType + ")";
activities.noDocument(_id, version);
continue;
@@ -467,7 +496,7 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
if (!activities.checkDateIndexable(createdDate)) {
Logging.connectors.warn("GridFS: Document " +
_id + " has a date excluded by the output connector (" + createdDate + ") -
skipping.");
- errorCode = activities.EXCLUDED_DATE;
+ errorCode = IProcessActivity.EXCLUDED_DATE;
errorDesc = "Excluded because of date (" +
createdDate + ")";
activities.noDocument(_id, version);
continue;
@@ -482,10 +511,10 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
String[] denyAclsArray = null;
if (acl != null) {
try {
- Object aclObject =
document.getMetaData().get(acl);
+ Object aclObject = metadata.get(acl);
if (aclObject != null) {
- List<String> acls = (List<String>)
aclObject;
- aclsArray = (String[]) acls.toArray();
+ List<String> acls =
toStringList(aclObject);
+ aclsArray = acls.toArray(new
String[0]);
}
} catch (ClassCastException e) {
// This is bad because security will fail
@@ -497,11 +526,12 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
}
if (denyAcl != null) {
try {
- Object denyAclObject =
document.getMetaData().get(denyAcl);
+ Object denyAclObject =
metadata.get(denyAcl);
if (denyAclObject != null) {
- List<String> denyAcls = (List<String>)
denyAclObject;
+ // Copy, so that the document's
metadata is not modified
+ List<String> denyAcls = new
ArrayList<String>(toStringList(denyAclObject));
denyAcls.add(GLOBAL_DENY_TOKEN);
- denyAclsArray = (String[])
denyAcls.toArray();
+ denyAclsArray = denyAcls.toArray(new
String[0]);
}
} catch (ClassCastException e) {
// This is bad because security will fail
@@ -513,45 +543,82 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
}
rd.setSecurity(RepositoryDocument.SECURITY_TYPE_DOCUMENT, aclsArray,
denyAclsArray);
- InputStream is = document.getInputStream();
- try {
+ try (InputStream is =
gfs.openDownloadStream(toBsonValue(document.get("_id")))) {
rd.setBinary(is, fileLenght);
- try {
-
activities.ingestDocumentWithException(_id, version, urlValue, rd);
- } catch (IOException e) {
- handleIOException(e);
- }
- } finally {
- try {
- is.close();
- } catch (IOException e) {
- handleIOException(e);
- }
+ activities.ingestDocumentWithException(_id,
version, urlValue, rd);
+ } catch (IOException e) {
+ handleIOException(e);
+ } catch (MongoException e) {
+ errorCode =
e.getClass().getSimpleName().toUpperCase(Locale.ROOT);
+ errorDesc = "Error reading GridFS content: " +
e.getMessage();
+ handleMongoException(e);
}
- gfs.getDB().getMongo().close();
- session = null;
errorCode = "OK";
} else {
Logging.connectors.warn("GridFS: Document " + _id
+ " has a invalid URL: " + urlValue + " - skipping.");
- errorCode = activities.BAD_URL;
+ errorCode = IProcessActivity.BAD_URL;
errorDesc = "Excluded because document had illegal
URL ('" + urlValue + "')";
activities.noDocument(_id, version);
}
} else {
Logging.connectors.warn("GridFS: Document " + _id + "
has a null URL - skipping.");
- errorCode = activities.NULL_URL;
+ errorCode = IProcessActivity.NULL_URL;
errorDesc = "Excluded because document had a null
URL.";
activities.noDocument(_id, version);
}
} finally {
if (errorCode != null) {
- activities.recordActivity(startTime, ACTIVITY_FETCH,
document.getLength(), _id, errorCode, errorDesc, null);
+ activities.recordActivity(startTime, ACTIVITY_FETCH,
fileLenght, _id, errorCode, errorDesc, null);
}
}
}
}
}
+ /**
+ * Read the file length from a GridFS files document (stored as int32 or
int64 depending on the writer).
+ */
+ protected static long getFileLength(Document fileDocument) {
+ Object length = fileDocument.get("length");
+ return (length instanceof Number) ? ((Number) length).longValue() : 0L;
+ }
+
+ /**
+ * Resolve the content type of a GridFS file. Legacy drivers stored it as
a top-level
+ * <code>contentType</code> field (deprecated by the GridFS spec), modern
drivers expect it
+ * inside the metadata document.
+ */
+ protected static String getContentType(Document fileDocument, Document
metadata) {
+ Object contentType =
fileDocument.get(GridFSConstants.CONTENT_TYPE_FIELD_NAME);
+ if (contentType == null && metadata != null) {
+ contentType =
metadata.get(GridFSConstants.CONTENT_TYPE_FIELD_NAME);
+ }
+ return contentType == null ? null : contentType.toString();
+ }
+
+ /**
+ * Convert a metadata field value into a list of strings.
+ *
+ * @throws ClassCastException if the value is not a list of strings.
+ */
+ protected static List<String> toStringList(Object value) {
+ List<?> list = (List<?>) value;
+ List<String> result = new ArrayList<String>(list.size());
+ for (Object item : list) {
+ result.add((String) item);
+ }
+ return result;
+ }
+
+ /**
+ * Convert a Java _id value into the BsonValue expected by the GridFS
download API.
+ */
+ protected static BsonValue toBsonValue(Object idValue) {
+ return new Document("_id", idValue)
+ .toBsonDocument(Document.class,
MongoClientSettings.getDefaultCodecRegistry())
+ .get("_id");
+ }
+
protected static void handleIOException(IOException e) throws
ManifoldCFException, ServiceInterruption {
if (e instanceof InterruptedIOException) {
throw new ManifoldCFException(e.getMessage(), e,
ManifoldCFException.INTERRUPTED);
@@ -560,6 +627,23 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
}
}
+ /**
+ * Map driver exceptions: network/timeout problems are transient and lead
to a retry,
+ * anything else is reported as an error.
+ */
+ protected void handleMongoException(MongoException e) throws
ManifoldCFException, ServiceInterruption {
+ if (e instanceof com.mongodb.MongoSocketException || e instanceof
com.mongodb.MongoTimeoutException
+ || e instanceof com.mongodb.MongoExecutionTimeoutException) {
+ closeSession();
+ long currentTime = System.currentTimeMillis();
+ Logging.connectors.warn("GridFS: Transient MongoDB error, retrying
later: " + e.getMessage(), e);
+ throw new ServiceInterruption("GridFS: Transient MongoDB error: "
+ e.getMessage(), e,
+ currentTime + 300000L, currentTime + 3L * 60L * 60000L,
-1, false);
+ }
+ Logging.connectors.error("GridFS: MongoDB error: " + e.getMessage(),
e);
+ throw new ManifoldCFException("GridFS: MongoDB error: " +
e.getMessage(), e);
+ }
+
/**
* Output the configuration header section. This method is called in the
* head section of the connector's configuration page. Its purpose is to
add
@@ -721,43 +805,31 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
throw new ManifoldCFException("GridFS: Metadata URL field
cannot be empty.");
}
- if (StringUtils.isEmpty(host) && StringUtils.isEmpty(port)) {
- try {
- session = new MongoClient().getDB(db);
- } catch (UnknownHostException ex) {
- throw new ManifoldCFException("GridFS: Default host is not
found. Does mongod process run?" + ex.getMessage(), ex);
- }
- } else if (!StringUtils.isEmpty(host) &&
StringUtils.isEmpty(port)) {
- try {
- session = new MongoClient(host).getDB(db);
- } catch (UnknownHostException ex) {
- throw new ManifoldCFException("GridFS: Given host
information is not valid or mongod process doesn't run" + ex.getMessage(), ex);
- }
- } else if (!StringUtils.isEmpty(host) &&
!StringUtils.isEmpty(port)) {
- try {
- int integerPort = Integer.parseInt(port);
- session = new MongoClient(host, integerPort).getDB(db);
- } catch (UnknownHostException ex) {
- throw new ManifoldCFException("GridFS: Given information
is not valid or mongod process doesn't run" + ex.getMessage(), ex);
- } catch (NumberFormatException ex) {
- throw new ManifoldCFException("GridFS: Given port is not
valid number. " + ex.getMessage(), ex);
- }
- } else if (StringUtils.isEmpty(host) &&
!StringUtils.isEmpty(port)) {
+ String effectiveHost = StringUtils.isEmpty(host) ?
ServerAddress.defaultHost() : host;
+ int effectivePort = ServerAddress.defaultPort();
+ if (!StringUtils.isEmpty(port)) {
try {
- int integerPort = Integer.parseInt(port);
- session = new MongoClient(host, integerPort).getDB(db);
- } catch (UnknownHostException ex) {
- throw new ManifoldCFException("GridFS: Given information
is not valid or mongod process doesn't run" + ex.getMessage(), ex);
+ effectivePort = Integer.parseInt(port);
} catch (NumberFormatException ex) {
throw new ManifoldCFException("GridFS: Given port is not
valid number. " + ex.getMessage(), ex);
}
}
+ final ServerAddress serverAddress = new
ServerAddress(effectiveHost, effectivePort);
+ MongoClientSettings.Builder settingsBuilder =
MongoClientSettings.builder()
+ .applyToClusterSettings(builder ->
builder.hosts(Collections.singletonList(serverAddress)));
+
if (!StringUtils.isEmpty(username) &&
!StringUtils.isEmpty(password)) {
- boolean auth = session.authenticate(username,
password.toCharArray());
- if (!auth) {
- throw new ManifoldCFException("GridFS: Given database
username and password doesn't match.");
- }
+ // Credentials are validated lazily by the driver, on the
first operation (see check())
+
settingsBuilder.credential(MongoCredential.createCredential(username, db,
password.toCharArray()));
+ }
+
+ try {
+ client = MongoClients.create(settingsBuilder.build());
+ session = client.getDatabase(db);
+ } catch (MongoException | IllegalArgumentException ex) {
+ closeSession();
+ throw new ManifoldCFException("GridFS: Given information is
not valid or mongod process doesn't run: " + ex.getMessage(), ex);
}
lastSessionFetch = System.currentTimeMillis();
}
@@ -807,12 +879,12 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
/**
* Special column names, as far as document queries are concerned
*/
- protected static HashMap documentKnownColumns;
+ protected static HashMap<String, String> documentKnownColumns;
static {
- documentKnownColumns = new HashMap();
- documentKnownColumns.put(GridFSConstants.DEFAULT_ID_FIELD_NAME, "");
- documentKnownColumns.put(GridFSConstants.URL_RETURN_FIELD_NAME_PARAM,
"");
+ documentKnownColumns = new HashMap<String, String>();
+ documentKnownColumns.put(GridFSConstants.DEFAULT_ID_FIELD_NAME,
StringUtils.EMPTY);
+ documentKnownColumns.put(GridFSConstants.URL_RETURN_FIELD_NAME_PARAM,
StringUtils.EMPTY);
}
/**
@@ -822,16 +894,15 @@ public class GridFSRepositoryConnector extends
BaseRepositoryConnector {
* @param metadataMap is the resultset row to use to get the metadata. All
* non-special columns from this row will be considered to be metadata.
*/
- protected void applyMetadata(RepositoryDocument rd, DBObject metadataMap)
+ protected void applyMetadata(RepositoryDocument rd, Document metadataMap)
throws ManifoldCFException {
// Cycle through the document's fields
- Iterator iter = metadataMap.keySet().iterator();
- while (iter.hasNext()) {
- String fieldName = (String) iter.next();
+ for (Map.Entry<String, Object> entry : metadataMap.entrySet()) {
+ String fieldName = entry.getKey();
if (documentKnownColumns.get(fieldName) == null) {
// Consider this field to contain metadata.
// We can only accept non-binary metadata at this time.
- Object metadata = metadataMap.get(fieldName);
+ Object metadata = entry.getValue();
if (!(metadata instanceof String)) {
throw new ManifoldCFException("Metadata field '" +
fieldName + "' must be convertible to a string.");
}
diff --git
a/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/Messages.java
b/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/Messages.java
index 5cf4976a1..1d29b620f 100644
---
a/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/Messages.java
+++
b/connectors/gridfs/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/gridfs/Messages.java
@@ -17,15 +17,9 @@ package org.apache.manifoldcf.crawler.connectors.gridfs;
import java.util.Locale;
import java.util.Map;
-import static org.apache.manifoldcf.core.i18n.Messages.getString;
+
import org.apache.manifoldcf.core.interfaces.IHTTPOutput;
import org.apache.manifoldcf.core.interfaces.ManifoldCFException;
-import static
org.apache.manifoldcf.ui.i18n.Messages.getAttributeJavascriptString;
-import static org.apache.manifoldcf.ui.i18n.Messages.getAttributeString;
-import static org.apache.manifoldcf.ui.i18n.Messages.getBodyJavascriptString;
-import static org.apache.manifoldcf.ui.i18n.Messages.getBodyString;
-import static org.apache.manifoldcf.ui.i18n.Messages.outputResource;
-import static
org.apache.manifoldcf.ui.i18n.Messages.outputResourceWithVelocity;
/**
*
diff --git
a/connectors/gridfs/connector/src/test/java/org/apache/manifoldcf/crawler/connectors/gridfs/tests/GridFSRepositoryConnectorHSQLDBIT.java
b/connectors/gridfs/connector/src/test/java/org/apache/manifoldcf/crawler/connectors/gridfs/tests/GridFSRepositoryConnectorHSQLDBIT.java
new file mode 100644
index 000000000..e50047248
--- /dev/null
+++
b/connectors/gridfs/connector/src/test/java/org/apache/manifoldcf/crawler/connectors/gridfs/tests/GridFSRepositoryConnectorHSQLDBIT.java
@@ -0,0 +1,318 @@
+/* $Id$ */
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.manifoldcf.crawler.connectors.gridfs.tests;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.manifoldcf.agents.interfaces.RepositoryDocument;
+import org.apache.manifoldcf.core.interfaces.ConfigParams;
+import
org.apache.manifoldcf.crawler.connectors.gridfs.GridFSRepositoryConnector;
+import org.apache.manifoldcf.crawler.interfaces.IProcessActivity;
+import org.apache.manifoldcf.crawler.interfaces.ISeedingActivity;
+import org.bson.Document;
+import org.bson.types.ObjectId;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.testcontainers.mongodb.MongoDBContainer;
+import org.testcontainers.utility.DockerImageName;
+
+import com.mongodb.MongoClientSettings;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
+import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.gridfs.GridFSBucket;
+import com.mongodb.client.gridfs.GridFSBuckets;
+import com.mongodb.client.gridfs.model.GridFSUploadOptions;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.Updates;
+
+/**
+ * Integration test for the GridFS repository connector against a real MongoDB
9.0.2
+ * instance started with Testcontainers (Docker is required).
+ */
+public class GridFSRepositoryConnectorHSQLDBIT extends BaseHSQLDB {
+
+ private static final String DATABASE = "gridfsTestDatabase";
+ private static final String BUCKET = "fs";
+ private static final String USERNAME = "gridfsuser";
+ private static final String PASSWORD = "gridfssecret";
+
+ private static MongoDBContainer mongoDBContainer;
+
+ private MongoClient testClient;
+ private MongoDatabase testDatabase;
+ private GridFSRepositoryConnector connector;
+
+ private ObjectId documentWithMetadataId;
+ private ObjectId legacyContentTypeId;
+ private ObjectId documentWithoutMetadataId;
+
+ @BeforeClass
+ public static void startMongoDB() {
+ mongoDBContainer = new
MongoDBContainer(DockerImageName.parse("mongo:9.0.2"));
+ mongoDBContainer.start();
+ }
+
+ @AfterClass
+ public static void stopMongoDB() {
+ if (mongoDBContainer != null) {
+ mongoDBContainer.stop();
+ mongoDBContainer = null;
+ }
+ }
+
+ @Before
+ public void setUpGridFS() throws Exception {
+ testClient = MongoClients.create(MongoClientSettings.builder()
+ .applyToClusterSettings(b -> b.hosts(Collections.singletonList(
+ new ServerAddress(mongoDBContainer.getHost(),
mongoDBContainer.getFirstMappedPort()))))
+ .build());
+ testDatabase = testClient.getDatabase(DATABASE);
+ GridFSBucket bucket = GridFSBuckets.create(testDatabase, BUCKET);
+
+ // Modern layout: content type stored in the metadata document
+ documentWithMetadataId = bucket.uploadFromStream("document.txt",
stream("Hello GridFS"),
+ new GridFSUploadOptions().metadata(new Document("url",
"http://www.example.com/document.txt")
+ .append("contentType", "text/plain")
+ .append("acl", Arrays.asList("user1", "group1"))
+ .append("denyAcl",
Collections.singletonList("user2"))));
+
+ // Legacy layout: content type stored as a top-level field of the
files document
+ legacyContentTypeId = bucket.uploadFromStream("legacy.html",
stream("<html></html>"),
+ new GridFSUploadOptions().metadata(new Document("url",
"http://www.example.com/legacy.html")));
+ testDatabase.getCollection(BUCKET + ".files")
+ .updateOne(Filters.eq("_id", legacyContentTypeId),
Updates.set("contentType", "text/html"));
+
+ // No metadata at all: must be skipped
+ documentWithoutMetadataId = bucket.uploadFromStream("nometadata.bin",
stream("binary"));
+
+ testDatabase.runCommand(new Document("createUser", USERNAME)
+ .append("pwd", PASSWORD)
+ .append("roles", Collections.singletonList(new
Document("role", "read").append("db", DATABASE))));
+
+ connector = new GridFSRepositoryConnector();
+ connector.connect(configParams(null, null));
+ }
+
+ @After
+ public void cleanUpGridFS() throws Exception {
+ if (connector != null) {
+ connector.disconnect();
+ connector = null;
+ }
+ if (testDatabase != null) {
+ try {
+ testDatabase.runCommand(new Document("dropUser", USERNAME));
+ } catch (Exception e) {
+ // ignore
+ }
+ testDatabase.drop();
+ testDatabase = null;
+ }
+ if (testClient != null) {
+ testClient.close();
+ testClient = null;
+ }
+ }
+
+ @Test
+ public void checkConnection() throws Exception {
+ assertEquals("Connection working", connector.check());
+ }
+
+ @Test
+ public void checkConnectionWithCredentials() throws Exception {
+ GridFSRepositoryConnector authenticated = new
GridFSRepositoryConnector();
+ authenticated.connect(configParams(USERNAME, PASSWORD));
+ try {
+ assertEquals("Connection working", authenticated.check());
+ } finally {
+ authenticated.disconnect();
+ }
+
+ GridFSRepositoryConnector wrongPassword = new
GridFSRepositoryConnector();
+ wrongPassword.connect(configParams(USERNAME, "wrong-password"));
+ try {
+ assertNotEquals("Connection working", wrongPassword.check());
+ } finally {
+ wrongPassword.disconnect();
+ }
+ }
+
+ @Test
+ public void seedsAllFiles() throws Exception {
+ ISeedingActivity seedingActivity = mock(ISeedingActivity.class);
+
+ connector.addSeedDocuments(seedingActivity, null, null,
System.currentTimeMillis(), 0);
+
+
verify(seedingActivity).addSeedDocument(documentWithMetadataId.toHexString());
+
verify(seedingActivity).addSeedDocument(legacyContentTypeId.toHexString());
+
verify(seedingActivity).addSeedDocument(documentWithoutMetadataId.toHexString());
+ verify(seedingActivity, times(3)).addSeedDocument(anyString());
+ }
+
+ @Test
+ public void processesDocuments() throws Exception {
+ IProcessActivity activities = indexEverythingActivity();
+ List<RepositoryDocument> ingested = new
ArrayList<RepositoryDocument>();
+ List<String> ingestedUris = new ArrayList<String>();
+ List<byte[]> ingestedContents = new ArrayList<byte[]>();
+ doAnswer(invocation -> {
+ RepositoryDocument rd = invocation.getArgument(3);
+ ingested.add(rd);
+ ingestedUris.add(invocation.getArgument(2));
+ ingestedContents.add(readAll(rd.getBinaryStream()));
+ return null;
+ }).when(activities).ingestDocumentWithException(anyString(),
anyString(), anyString(), any(RepositoryDocument.class));
+
+ String missingId = new ObjectId().toHexString();
+ connector.processDocuments(new String[]{
+ documentWithMetadataId.toHexString(),
+ legacyContentTypeId.toHexString(),
+ documentWithoutMetadataId.toHexString(),
+ missingId
+ }, null, null, activities, 0, true);
+
+ // Two documents ingested, in order
+ assertEquals(2, ingested.size());
+
+ RepositoryDocument modern = ingested.get(0);
+ assertEquals("http://www.example.com/document.txt",
ingestedUris.get(0));
+ assertEquals("document.txt", modern.getFileName());
+ assertEquals("text/plain", modern.getMimeType());
+ assertEquals("Hello GridFS", new String(ingestedContents.get(0),
StandardCharsets.UTF_8));
+ assertArrayEquals(new String[]{"user1", "group1"},
+
modern.getSecurityACL(RepositoryDocument.SECURITY_TYPE_DOCUMENT));
+ String[] denyAcls =
modern.getSecurityDenyACL(RepositoryDocument.SECURITY_TYPE_DOCUMENT);
+ assertNotNull(denyAcls);
+ assertEquals(2, denyAcls.length);
+ assertEquals("user2", denyAcls[0]);
+
+ RepositoryDocument legacy = ingested.get(1);
+ assertEquals("http://www.example.com/legacy.html",
ingestedUris.get(1));
+ assertEquals("text/html", legacy.getMimeType());
+ assertEquals("<html></html>", new String(ingestedContents.get(1),
StandardCharsets.UTF_8));
+
+ // No metadata: skipped; missing file: deleted
+
verify(activities).noDocument(eq(documentWithoutMetadataId.toHexString()),
anyString());
+ verify(activities).deleteDocument(missingId);
+ verify(activities,
never()).deleteDocument(documentWithMetadataId.toHexString());
+ }
+
+ @Test
+ public void versionStringChangesWhenMetadataChanges() throws Exception {
+ String id = documentWithMetadataId.toHexString();
+ List<String> versions = new ArrayList<String>();
+ IProcessActivity activities = mock(IProcessActivity.class);
+ when(activities.checkDocumentNeedsReindexing(anyString(),
anyString())).thenAnswer(invocation -> {
+ versions.add(invocation.getArgument(1));
+ return false;
+ });
+
+ connector.processDocuments(new String[]{id}, null, null, activities,
0, true);
+ connector.processDocuments(new String[]{id}, null, null, activities,
0, true);
+ testDatabase.getCollection(BUCKET + ".files")
+ .updateOne(Filters.eq("_id", documentWithMetadataId),
Updates.set("metadata.url", "http://www.example.com/moved.txt"));
+ connector.processDocuments(new String[]{id}, null, null, activities,
0, true);
+
+ assertEquals(3, versions.size());
+ assertFalse(versions.get(0).isEmpty());
+ assertEquals("Version must be stable", versions.get(0),
versions.get(1));
+ assertNotEquals("Version must change with metadata", versions.get(1),
versions.get(2));
+ verify(activities, never()).ingestDocumentWithException(anyString(),
anyString(), anyString(), any(RepositoryDocument.class));
+ }
+
+ @Test
+ public void sessionLifecycle() throws Exception {
+ assertFalse(connector.isConnected());
+ connector.addSeedDocuments(mock(ISeedingActivity.class), null, null,
System.currentTimeMillis(), 0);
+ assertTrue(connector.isConnected());
+ connector.disconnect();
+ assertFalse(connector.isConnected());
+ connector = null;
+ }
+
+ private IProcessActivity indexEverythingActivity() throws Exception {
+ IProcessActivity activities = mock(IProcessActivity.class);
+ when(activities.checkDocumentNeedsReindexing(anyString(),
anyString())).thenReturn(true);
+ when(activities.checkURLIndexable(any())).thenReturn(true);
+ when(activities.checkLengthIndexable(anyLong())).thenReturn(true);
+ when(activities.checkMimeTypeIndexable(any())).thenReturn(true);
+ when(activities.checkDateIndexable(any())).thenReturn(true);
+ return activities;
+ }
+
+ private ConfigParams configParams(String username, String password) {
+ ConfigParams params = new ConfigParams();
+ params.setParameter("host", mongoDBContainer.getHost());
+ params.setParameter("port",
String.valueOf(mongoDBContainer.getFirstMappedPort()));
+ params.setParameter("db", DATABASE);
+ params.setParameter("bucket", BUCKET);
+ params.setParameter("url", "url");
+ params.setParameter("acl", "acl");
+ params.setParameter("denyAcl", "denyAcl");
+ if (username != null) {
+ params.setParameter("username", username);
+ params.setObfuscatedParameter("password", password);
+ }
+ return params;
+ }
+
+ private static InputStream stream(String content) {
+ return new
ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
+ }
+
+ private static byte[] readAll(InputStream is) throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buffer = new byte[8192];
+ int read;
+ while ((read = is.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ }
+ return out.toByteArray();
+ }
+}
diff --git a/connectors/gridfs/pom.xml b/connectors/gridfs/pom.xml
index 410ce86df..7acf8e8bd 100644
--- a/connectors/gridfs/pom.xml
+++ b/connectors/gridfs/pom.xml
@@ -59,6 +59,33 @@
<workingDirectory>target/test-output</workingDirectory>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/*Postgresql*.java</exclude>
+ <exclude>**/*MySQL*.java</exclude>
+ </excludes>
+ <forkCount>1</forkCount>
+ <reuseForks>false</reuseForks>
+ <workingDirectory>target/test-output</workingDirectory>
+ </configuration>
+ <executions>
+ <execution>
+ <id>integration-test</id>
+ <goals>
+ <goal>integration-test</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>verify</id>
+ <goals>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
@@ -135,8 +162,18 @@
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
- <artifactId>mongo-java-driver</artifactId>
- <version>2.11.3</version>
+ <artifactId>mongodb-driver-sync</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-mongodb</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
</dependency>
</dependencies>
</project>
diff --git a/connectors/mongodb/test-materials/README.txt
b/connectors/gridfs/test-materials/README.txt
similarity index 80%
copy from connectors/mongodb/test-materials/README.txt
copy to connectors/gridfs/test-materials/README.txt
index 4be9d6c1b..bf08d6249 100644
--- a/connectors/mongodb/test-materials/README.txt
+++ b/connectors/gridfs/test-materials/README.txt
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-To test this connector, download the Embedded MongoDB dependencies using "ant
download-dependencies"
+To test this connector, download the Testcontainers dependencies using "ant
download-dependencies"
or the global "ant make-deps" target, and use the "ant test" target for the
standard ant build.
-You can read more about the process on the "how-to-build-and-deploy.html"
documentation page.
\ No newline at end of file
+The integration tests start MongoDB 9.0.2 as a Docker container, so a running
Docker daemon is required.
+You can read more about the process on the "how-to-build-and-deploy.html"
documentation page.
diff --git a/connectors/mongodb/build.xml b/connectors/mongodb/build.xml
index 58441ba1a..a88be2b2a 100644
--- a/connectors/mongodb/build.xml
+++ b/connectors/mongodb/build.xml
@@ -1,153 +1,122 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You 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.
--->
-
-<project name="mongodb" default="all">
-
- <property environment="env"/>
- <condition property="mcf-dist" value="${env.MCFDISTPATH}">
- <isset property="env.MCFDISTPATH"/>
- </condition>
- <property name="abs-dist" location="../../dist"/>
- <condition property="mcf-dist" value="${abs-dist}">
- <not>
- <isset property="env.MCFDISTPATH"/>
- </not>
- </condition>
-
- <import file="${mcf-dist}/connector-build.xml"/>
-
- <target name="download-cleanup">
- <delete>
- <fileset dir="test-materials" excludes="README*.txt"/>
- </delete>
- </target>
-
- <path id="connector-classpath">
- <path refid="mcf-connector-build.connector-classpath"/>
- <fileset dir="../../lib">
- <include name="mongo-java-driver*.jar"/>
- </fileset>
- </path>
-
- <target name="lib" depends="mcf-connector-build.lib,precompile-check"
if="canBuild">
- <mkdir dir="dist/lib"/>
- <copy todir="dist/lib">
- <fileset dir="../../lib">
- <include name="mongo-java-driver*.jar"/>
- </fileset>
- </copy>
- </target>
-
- <target name="deliver-connector"
depends="mcf-connector-build.deliver-connector">
- <antcall target="general-add-output-connector">
- <param name="connector-label" value="MongoDB"/>
- <param name="connector-class"
-
value="org.apache.manifoldcf.agents.output.mongodboutput.MongodbOutputConnector"/>
- </antcall>
- </target>
-
- <property name="embedded-mongodb.version.label" value="4.33.0"/>
- <property name="embedded-mongodb.artifactId"
value="de.flapdoodle.embed.mongo"/>
- <property name="embedded-mongodb-package" value="de/flapdoodle/embed"/>
- <property name="embedded-mongodb.snapshot-jar"
value="${embedded-mongodb.artifactId}-${embedded-mongodb.version.label}.jar"/>
-
- <property name="embedded-mongodb.process.version.label" value="4.18.0"/>
- <property name="embedded-mongodb.process.artifactId"
value="de.flapdoodle.embed.process"/>
- <property name="embedded-mongodb.process-package"
value="de/flapdoodle/embed"/>
- <property name="embedded-mongodb.process.jar"
value="${embedded-mongodb.process.artifactId}-${embedded-mongodb.process.version.label}.jar"/>
-
- <property name="embedded-mongodb.packageresolver.version.label"
value="4.33.0"/>
- <property name="embedded-mongodb.packageresolver.artifactId"
value="de.flapdoodle.embed.mongo.packageresolver"/>
- <property name="embedded-mongodb-packageresolver-package"
value="de/flapdoodle/embed"/>
- <property name="embedded-mongodb.packageresolver.jar"
value="${embedded-mongodb.packageresolver.artifactId}-${embedded-mongodb.packageresolver.version.label}.jar"/>
-
- <property name="embedded-mongodb.os.version.label" value="1.11.0"/>
- <property name="embedded-mongodb.os.artifactId" value="de.flapdoodle.os"/>
- <property name="embedded-mongodb-os-package" value="de/flapdoodle"/>
- <property name="embedded-mongodb.os.jar"
value="${embedded-mongodb.os.artifactId}-${embedded-mongodb.os.version.label}.jar"/>
-
- <property name="embedded-mongodb.os-api.version.label" value="1.7.1"/>
- <property name="embedded-mongodb.os-api.artifactId"
value="de.flapdoodle.os-api"/>
- <property name="embedded-mongodb-os-api-package" value="de/flapdoodle"/>
- <property name="embedded-mongodb.os-api.jar"
value="${embedded-mongodb.os-api.artifactId}-${embedded-mongodb.os-api.version.label}.jar"/>
-
- <property name="embedded-mongodb.reverse.version.label" value="1.9.1"/>
- <property name="embedded-mongodb.reverse.artifactId"
value="de.flapdoodle.reverse"/>
- <property name="embedded-mongodb-reverse-package"
value="de/flapdoodle/reverse"/>
- <property name="embedded-mongodb.reverse.jar"
value="${embedded-mongodb.reverse.artifactId}-${embedded-mongodb.reverse.version.label}.jar"/>
-
- <property name="embedded-mongodb.graph.version.label" value="1.3.4"/>
- <property name="embedded-mongodb.graph.artifactId"
value="de.flapdoodle.graph"/>
- <property name="embedded-mongodb-graph-package"
value="de/flapdoodle/graph"/>
- <property name="embedded-mongodb.graph.jar"
value="${embedded-mongodb.graph.artifactId}-${embedded-mongodb.graph.version.label}.jar"/>
-
- <property name="embedded-mongodb.java8.version.label" value="1.7.2"/>
- <property name="embedded-mongodb.java8.artifactId"
value="de.flapdoodle.java8"/>
- <property name="embedded-mongodb-java8-package"
value="de/flapdoodle/java8"/>
- <property name="embedded-mongodb.java8.jar"
value="${embedded-mongodb.java8.artifactId}-${embedded-mongodb.java8.version.label}.jar"/>
-
- <property name="jgrapht-core.version.label" value="1.4.0"/>
- <property name="jgrapht-core.artifactId" value="jgrapht-core"/>
- <property name="jgrapht-core-package" value="org/jgrapht"/>
- <property name="jgrapht-core.jar"
value="${jgrapht-core.artifactId}-${jgrapht-core.version.label}.jar"/>
-
- <property name="jheaps.version.label" value="0.11"/>
- <property name="jheaps.artifactId" value="jheaps"/>
- <property name="jheaps-package" value="org/jheaps"/>
- <property name="jheaps.jar"
value="${jheaps.artifactId}-${jheaps.version.label}.jar"/>
-
- <target name="calculate-testcode-condition">
- <available file="test-materials/${embedded-mongodb.snapshot-jar}"
property="tests-present"/>
- <available file="test-materials/${embedded-mongodb.process.jar}"
property="tests-present"/>
- <available
file="test-materials/${embedded-mongodb.packageresolver.jar}"
property="tests-present"/>
- <available file="test-materials/${embedded-mongodb.os.jar}"
property="tests-present"/>
- <available file="test-materials/${embedded-mongodb.os-api.jar}"
property="tests-present"/>
- <available file="test-materials/${embedded-mongodb.reverse.jar}"
property="tests-present"/>
- <available file="test-materials/${embedded-mongodb.graph.jar}"
property="tests-present"/>
- <available file="test-materials/${embedded-mongodb.java8.jar}"
property="tests-present"/>
- <available file="test-materials/${jgrapht-core.jar}"
property="tests-present"/>
- <available file="test-materials/${jheaps.jar}"
property="tests-present"/>
-
- <copy todir="${mcf-dist}/test-lib">
- <fileset dir="test-materials">
- <include name="de.flapdoodle*.jar"/>
- <include name="jgrapht-core*.jar"/>
- <include name="jheaps*.jar"/>
- </fileset>
- </copy>
- </target>
-
- <target name="pretest-warn" depends="calculate-testcode-condition"
unless="tests-present">
- <echo message="Need Embedded MongoDB download in order to run tests -
run ant make-deps"/>
- </target>
-
- <target name="download-dependencies">
- <property name="sonatype-maven-repo-releases"
value="https://oss.sonatype.org/content/repositories/public" />
- <property name="apache-maven-repo-releases"
value="https://repo1.maven.org/maven2" />
- <get
src="${apache-maven-repo-releases}/${embedded-mongodb-package}/${embedded-mongodb.artifactId}/${embedded-mongodb.version.label}/${embedded-mongodb.snapshot-jar}"
dest="test-materials/${embedded-mongodb.snapshot-jar}"/>
- <get
src="${apache-maven-repo-releases}/${embedded-mongodb.process-package}/${embedded-mongodb.process.artifactId}/${embedded-mongodb.process.version.label}/${embedded-mongodb.process.jar}"
dest="test-materials/${embedded-mongodb.process.jar}"/>
- <get
src="${apache-maven-repo-releases}/${embedded-mongodb-packageresolver-package}/${embedded-mongodb.packageresolver.artifactId}/${embedded-mongodb.packageresolver.version.label}/${embedded-mongodb.packageresolver.jar}"
dest="test-materials/${embedded-mongodb.packageresolver.jar}"/>
- <get
src="${apache-maven-repo-releases}/${embedded-mongodb-os-package}/${embedded-mongodb.os.artifactId}/${embedded-mongodb.os.version.label}/${embedded-mongodb.os.jar}"
dest="test-materials/${embedded-mongodb.os.jar}"/>
- <get
src="${apache-maven-repo-releases}/${embedded-mongodb-os-api-package}/${embedded-mongodb.os-api.artifactId}/${embedded-mongodb.os-api.version.label}/${embedded-mongodb.os-api.jar}"
dest="test-materials/${embedded-mongodb.os-api.jar}"/>
- <get
src="${apache-maven-repo-releases}/${embedded-mongodb-reverse-package}/${embedded-mongodb.reverse.artifactId}/${embedded-mongodb.reverse.version.label}/${embedded-mongodb.reverse.jar}"
dest="test-materials/${embedded-mongodb.reverse.jar}"/>
- <get
src="${apache-maven-repo-releases}/${embedded-mongodb-graph-package}/${embedded-mongodb.graph.artifactId}/${embedded-mongodb.graph.version.label}/${embedded-mongodb.graph.jar}"
dest="test-materials/${embedded-mongodb.graph.jar}"/>
- <get
src="${apache-maven-repo-releases}/${embedded-mongodb-java8-package}/${embedded-mongodb.java8.artifactId}/${embedded-mongodb.java8.version.label}/${embedded-mongodb.java8.jar}"
dest="test-materials/${embedded-mongodb.java8.jar}"/>
- <get
src="${apache-maven-repo-releases}/${jgrapht-core-package}/${jgrapht-core.artifactId}/${jgrapht-core.version.label}/${jgrapht-core.jar}"
dest="test-materials/${jgrapht-core.jar}"/>
- <get
src="${apache-maven-repo-releases}/${jheaps-package}/${jheaps.artifactId}/${jheaps.version.label}/${jheaps.jar}"
dest="test-materials/${jheaps.jar}"/>
- </target>
-
-</project>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+
+<project name="mongodb" default="all">
+
+ <property environment="env"/>
+ <condition property="mcf-dist" value="${env.MCFDISTPATH}">
+ <isset property="env.MCFDISTPATH"/>
+ </condition>
+ <property name="abs-dist" location="../../dist"/>
+ <condition property="mcf-dist" value="${abs-dist}">
+ <not>
+ <isset property="env.MCFDISTPATH"/>
+ </not>
+ </condition>
+
+ <import file="${mcf-dist}/connector-build.xml"/>
+
+ <target name="download-cleanup">
+ <delete>
+ <fileset dir="test-materials" excludes="README*.txt"/>
+ </delete>
+ </target>
+
+ <!-- MongoDB sync driver (MongoDB 9.x compliant) -->
+ <patternset id="mongodb-driver-jars">
+ <include name="mongodb-driver-sync-*.jar"/>
+ <include name="mongodb-driver-core-*.jar"/>
+ <include name="bson-*.jar"/>
+ </patternset>
+
+ <path id="connector-classpath">
+ <path refid="mcf-connector-build.connector-classpath"/>
+ <fileset dir="../../lib">
+ <patternset refid="mongodb-driver-jars"/>
+ </fileset>
+ </path>
+
+ <target name="lib" depends="mcf-connector-build.lib,precompile-check"
if="canBuild">
+ <mkdir dir="dist/lib"/>
+ <copy todir="dist/lib">
+ <fileset dir="../../lib">
+ <patternset refid="mongodb-driver-jars"/>
+ </fileset>
+ </copy>
+ </target>
+
+ <target name="deliver-connector"
depends="mcf-connector-build.deliver-connector">
+ <antcall target="general-add-output-connector">
+ <param name="connector-label" value="MongoDB"/>
+ <param name="connector-class"
+
value="org.apache.manifoldcf.agents.output.mongodboutput.MongodbOutputConnector"/>
+ </antcall>
+ </target>
+
+ <!-- Test materials: Testcontainers is used to run MongoDB 9.0.2 as a
Docker container during the tests -->
+ <property name="testcontainers.version" value="2.0.5"/>
+ <property name="docker-java.version" value="3.7.1"/>
+ <property name="duct-tape.version" value="1.0.8"/>
+ <property name="jna.version" value="5.10.0"/>
+ <property name="commons-compress.version" value="1.28.0"/>
+ <property name="jetbrains-annotations.version" value="17.0.0"/>
+ <property name="jackson-annotations.version" value="2.21"/>
+
+ <!-- Test materials come first so that the newer jars required by
Testcontainers
+ (e.g. jackson-annotations, commons-compress) take precedence over the
older shared ones -->
+ <path id="connector-test-classpath">
+ <fileset dir="test-materials" includes="*.jar"
erroronmissingdir="false"/>
+ <path refid="mcf-connector-build.connector-test-classpath"/>
+ </path>
+
+ <target name="calculate-testcode-condition">
+ <condition property="tests-present">
+ <and>
+ <available
file="test-materials/testcontainers-${testcontainers.version}.jar"/>
+ <available
file="test-materials/testcontainers-mongodb-${testcontainers.version}.jar"/>
+ <available
file="test-materials/docker-java-api-${docker-java.version}.jar"/>
+ <available
file="test-materials/docker-java-transport-${docker-java.version}.jar"/>
+ <available
file="test-materials/docker-java-transport-zerodep-${docker-java.version}.jar"/>
+ <available
file="test-materials/duct-tape-${duct-tape.version}.jar"/>
+ <available file="test-materials/jna-${jna.version}.jar"/>
+ <available
file="test-materials/commons-compress-${commons-compress.version}.jar"/>
+ <available
file="test-materials/annotations-${jetbrains-annotations.version}.jar"/>
+ <available
file="test-materials/jackson-annotations-${jackson-annotations.version}.jar"/>
+ </and>
+ </condition>
+ </target>
+
+ <target name="pretest-warn" depends="calculate-testcode-condition"
unless="tests-present">
+ <echo message="Need Testcontainers download in order to run tests -
run ant make-deps (Docker is required to run MongoDB 9.0.2)"/>
+ </target>
+
+ <target name="download-dependencies">
+ <property name="maven-repo" value="https://repo1.maven.org/maven2"/>
+ <mkdir dir="test-materials"/>
+ <get
src="${maven-repo}/org/testcontainers/testcontainers/${testcontainers.version}/testcontainers-${testcontainers.version}.jar"
dest="test-materials/testcontainers-${testcontainers.version}.jar"/>
+ <get
src="${maven-repo}/org/testcontainers/testcontainers-mongodb/${testcontainers.version}/testcontainers-mongodb-${testcontainers.version}.jar"
dest="test-materials/testcontainers-mongodb-${testcontainers.version}.jar"/>
+ <get
src="${maven-repo}/com/github/docker-java/docker-java-api/${docker-java.version}/docker-java-api-${docker-java.version}.jar"
dest="test-materials/docker-java-api-${docker-java.version}.jar"/>
+ <get
src="${maven-repo}/com/github/docker-java/docker-java-transport/${docker-java.version}/docker-java-transport-${docker-java.version}.jar"
dest="test-materials/docker-java-transport-${docker-java.version}.jar"/>
+ <get
src="${maven-repo}/com/github/docker-java/docker-java-transport-zerodep/${docker-java.version}/docker-java-transport-zerodep-${docker-java.version}.jar"
dest="test-materials/docker-java-transport-zerodep-${docker-java.version}.jar"/>
+ <get
src="${maven-repo}/org/rnorth/duct-tape/duct-tape/${duct-tape.version}/duct-tape-${duct-tape.version}.jar"
dest="test-materials/duct-tape-${duct-tape.version}.jar"/>
+ <get
src="${maven-repo}/net/java/dev/jna/jna/${jna.version}/jna-${jna.version}.jar"
dest="test-materials/jna-${jna.version}.jar"/>
+ <get
src="${maven-repo}/org/apache/commons/commons-compress/${commons-compress.version}/commons-compress-${commons-compress.version}.jar"
dest="test-materials/commons-compress-${commons-compress.version}.jar"/>
+ <get
src="${maven-repo}/org/jetbrains/annotations/${jetbrains-annotations.version}/annotations-${jetbrains-annotations.version}.jar"
dest="test-materials/annotations-${jetbrains-annotations.version}.jar"/>
+ <get
src="${maven-repo}/com/fasterxml/jackson/core/jackson-annotations/${jackson-annotations.version}/jackson-annotations-${jackson-annotations.version}.jar"
dest="test-materials/jackson-annotations-${jackson-annotations.version}.jar"/>
+ </target>
+
+</project>
diff --git
a/connectors/mongodb/connector/src/main/java/org/apache/manifoldcf/agents/output/mongodboutput/MongodbOutputConnector.java
b/connectors/mongodb/connector/src/main/java/org/apache/manifoldcf/agents/output/mongodboutput/MongodbOutputConnector.java
index 3022700e9..861c23ff7 100644
---
a/connectors/mongodb/connector/src/main/java/org/apache/manifoldcf/agents/output/mongodboutput/MongodbOutputConnector.java
+++
b/connectors/mongodb/connector/src/main/java/org/apache/manifoldcf/agents/output/mongodboutput/MongodbOutputConnector.java
@@ -16,18 +16,19 @@
*/
package org.apache.manifoldcf.agents.output.mongodboutput;
-import com.mongodb.BasicDBObject;
-import com.mongodb.DB;
-import com.mongodb.DBCursor;
-import com.mongodb.DBCollection;
-import com.mongodb.MongoClient;
-import com.mongodb.DBTCPConnector;
-import com.mongodb.DBPort;
-import com.mongodb.DBPortPool;
-import com.mongodb.WriteConcern;
-import com.mongodb.WriteResult;
-import com.mongodb.MongoException;
-import org.bson.types.Binary;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.net.ConnectException;
+import java.rmi.RemoteException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
@@ -36,27 +37,30 @@ import
org.apache.manifoldcf.agents.interfaces.IOutputRemoveActivity;
import org.apache.manifoldcf.agents.interfaces.RepositoryDocument;
import org.apache.manifoldcf.agents.interfaces.ServiceInterruption;
import org.apache.manifoldcf.agents.output.BaseOutputConnector;
-import org.apache.manifoldcf.core.interfaces.IThreadContext;
+import org.apache.manifoldcf.core.interfaces.ConfigParams;
import org.apache.manifoldcf.core.interfaces.IHTTPOutput;
import org.apache.manifoldcf.core.interfaces.IPasswordMapperActivity;
import org.apache.manifoldcf.core.interfaces.IPostParameters;
-import org.apache.manifoldcf.core.interfaces.ConfigParams;
-import org.apache.manifoldcf.core.interfaces.VersionContext;
+import org.apache.manifoldcf.core.interfaces.IThreadContext;
import org.apache.manifoldcf.core.interfaces.ManifoldCFException;
+import org.apache.manifoldcf.core.interfaces.VersionContext;
import org.apache.manifoldcf.crawler.system.Logging;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+import org.bson.types.Binary;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.net.ConnectException;
-import java.net.UnknownHostException;
-import java.rmi.RemoteException;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Date;
-import java.util.Iterator;
+import com.mongodb.MongoClientSettings;
+import com.mongodb.MongoCredential;
+import com.mongodb.MongoException;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.ReplaceOptions;
+import com.mongodb.client.result.DeleteResult;
+import com.mongodb.client.result.UpdateResult;
/**
* This is the "output connector" for MongoDB.
@@ -152,7 +156,7 @@ public class MongodbOutputConnector extends
BaseOutputConnector {
/**
* MongoDB database handle instance
*/
- private DB mongoDatabase = null;
+ private MongoDatabase mongoDatabase = null;
/**
@@ -226,11 +230,15 @@ public class MongodbOutputConnector extends
BaseOutputConnector {
*/
@Override
public boolean isConnected() {
- if (client == null) {
+ if (client == null || mongoDatabase == null) {
+ return false;
+ }
+ try {
+ mongoDatabase.runCommand(new Document("ping", 1));
+ return true;
+ } catch (Exception e) {
return false;
}
- DBTCPConnector currentTCPConnection = client.getConnector();
- return currentTCPConnection.isOpen();
}
/**
@@ -642,55 +650,30 @@ public class MongodbOutputConnector extends
BaseOutputConnector {
try {
// Create a session
if (client == null) {
- if (StringUtils.isEmpty(host) &&
StringUtils.isEmpty(port)) {
- try {
- client = new MongoClient();
- mongoDatabase = client.getDB(database);
- } catch (UnknownHostException ex) {
- throw new ManifoldCFException("MongoDB: Default
host is not found. Is mongod process running?" + ex.getMessage(), ex);
- }
- } else if (!StringUtils.isEmpty(host) &&
StringUtils.isEmpty(port)) {
- try {
- client = new MongoClient(host);
- mongoDatabase = client.getDB(database);
- } catch (UnknownHostException ex) {
- throw new ManifoldCFException("MongoDB: Given host
information is not valid or mongod process doesn't run" + ex.getMessage(), ex);
- }
- } else if (!StringUtils.isEmpty(host) &&
!StringUtils.isEmpty(port)) {
+ String effectiveHost = StringUtils.isEmpty(host) ?
"localhost" : host;
+ int effectivePort = 27017;
+ if (!StringUtils.isEmpty(port)) {
try {
- int integerPort = Integer.parseInt(port);
- client = new MongoClient(host, integerPort);
- mongoDatabase = client.getDB(database);
- } catch (UnknownHostException ex) {
- throw new ManifoldCFException("MongoDB: Given
information is not valid or mongod process doesn't run" + ex.getMessage(), ex);
+ effectivePort = Integer.parseInt(port);
} catch (NumberFormatException ex) {
throw new ManifoldCFException("MongoDB: Given port
is not a valid number. " + ex.getMessage(), ex);
}
- } else if (StringUtils.isEmpty(host) &&
!StringUtils.isEmpty(port)) {
- try {
- int integerPort = Integer.parseInt(port);
- client = new MongoClient("localhost", integerPort);
- mongoDatabase = client.getDB(database);
- } catch (UnknownHostException e) {
- Logging.connectors.warn("MongoDB: Given
information is not valid or mongod process doesn't run" + e.getMessage(), e);
- throw new ManifoldCFException("MongoDB: Given
information is not valid or mongod process doesn't run" + e.getMessage(), e);
- } catch (NumberFormatException e) {
- Logging.connectors.warn("MongoDB: Given port is
not valid number. " + e.getMessage(), e);
- throw new ManifoldCFException("MongoDB: Given port
is not valid number. " + e.getMessage(), e);
- }
}
+
+ final String finalHost = effectiveHost;
+ final int finalPort = effectivePort;
+ MongoClientSettings.Builder settingsBuilder =
MongoClientSettings.builder()
+ .applyToClusterSettings(builder ->
+ builder.hosts(Collections.singletonList(new
ServerAddress(finalHost, finalPort)))
+ );
+
if (!StringUtils.isEmpty(username) &&
!StringUtils.isEmpty(password)) {
- boolean auth = mongoDatabase.authenticate(username,
password.toCharArray());
- if (!auth) {
- Logging.connectors.warn("MongoDB:Authentication
Error! Given database username and password doesn't match for the database
given.");
- throw new ManifoldCFException("MongoDB: Given
database username and password doesn't match for the database given.");
- } else {
- if (Logging.connectors.isDebugEnabled()) {
- Logging.connectors.debug("MongoDB: Username =
'" + username + "'");
- Logging.connectors.debug("MongoDB: Password
exists");
- }
- }
+ MongoCredential credential =
MongoCredential.createCredential(username, database, password.toCharArray());
+ settingsBuilder.credential(credential);
}
+
+ client = MongoClients.create(settingsBuilder.build());
+ mongoDatabase = client.getDatabase(database);
}
} catch (Throwable e) {
@@ -713,12 +696,8 @@ public class MongodbOutputConnector extends
BaseOutputConnector {
public void run() {
try {
- if (client != null) {
- DBTCPConnector dbtcpConnector = client.getConnector();
- DBPortPool dbPortPool =
dbtcpConnector.getDBPortPool(client.getAddress());
- DBPort dbPort = dbPortPool.get();
- dbPort.ensureOpen();
- client = null;
+ if (client != null && mongoDatabase != null) {
+ mongoDatabase.runCommand(new Document("ping", 1));
}
} catch (Throwable e) {
@@ -743,6 +722,9 @@ public class MongodbOutputConnector extends
BaseOutputConnector {
public void run() {
try {
+ if (client != null) {
+ client.close();
+ }
client = null;
mongoDatabase = null;
} catch (Throwable e) {
@@ -763,9 +745,6 @@ public class MongodbOutputConnector extends
BaseOutputConnector {
getSession();
- //to get an exception if write fails
- client.setWriteConcern(WriteConcern.SAFE);
-
long startTime = System.currentTimeMillis();
String resultDescription = StringUtils.EMPTY;
@@ -792,9 +771,9 @@ public class MongodbOutputConnector extends
BaseOutputConnector {
byte[] bytes = IOUtils.toByteArray(inputStream);
Binary content = new Binary(bytes);
- DBCollection mongoCollection =
mongoDatabase.getCollection(collection);
+ MongoCollection<Document> mongoCollection =
mongoDatabase.getCollection(collection);
- BasicDBObject newDocument = new BasicDBObject();
+ Document newDocument = new Document();
newDocument.append("fileName", fileName)
.append("creationDate", creationDate)
.append("lastModificationDate", lastModificationDate)
@@ -811,30 +790,21 @@ public class MongodbOutputConnector extends
BaseOutputConnector {
String fieldName = i.next();
Date[] dateFieldValues = document.getFieldAsDates(fieldName);
if (dateFieldValues != null) {
- newDocument.append(fieldName, dateFieldValues);
+ newDocument.append(fieldName,
Arrays.asList(dateFieldValues));
} else {
String[] fieldValues =
document.getFieldAsStrings(fieldName);
- newDocument.append(fieldName, fieldValues);
+ newDocument.append(fieldName, Arrays.asList(fieldValues));
}
}
- BasicDBObject searchQuery = new
BasicDBObject().append("documentURI", documentURI);
- DBCursor cursor = mongoCollection.find(searchQuery);
- Long numberOfDocumentsBeforeInsert = mongoCollection.count();
- WriteResult result;
- if (cursor.count() > 0) {
- result = mongoCollection.update(searchQuery, newDocument);
- } else {
- result = mongoCollection.insert(newDocument);
- }
- //result.getLastError().get("err") == null
- //To get the number of documents indexed i.e. tne number of
documents inserted or updated
- Long numberOfDocumentsAfterInsert = mongoCollection.count();
- Long numberOfDocumentsInserted = numberOfDocumentsAfterInsert -
numberOfDocumentsBeforeInsert;
- Long numberOfDocumentsIndexed = (numberOfDocumentsInserted != 0) ?
numberOfDocumentsInserted : result.getN();
+ Bson searchQuery = Filters.eq("documentURI", documentURI);
+ ReplaceOptions replaceOptions = new ReplaceOptions().upsert(true);
+ UpdateResult result = mongoCollection.replaceOne(searchQuery,
newDocument, replaceOptions);
+
+ long numberOfDocumentsIndexed = (result.getUpsertedId() != null) ?
1L : (result.getMatchedCount() > 0 ? 1L : 0L);
Logging.connectors.info("Number of documents indexed : " +
numberOfDocumentsIndexed);
- //check if a document is inserted or updated
(numberOfDocumentsInserted > 0) || (result.getN() > 0)
+ //check if a document is inserted or updated
if (numberOfDocumentsIndexed > 0) {
resultDescription = DOCUMENT_STATUS_ACCEPTED_DESC;
return DOCUMENT_STATUS_ACCEPTED;
@@ -874,15 +844,14 @@ public class MongodbOutputConnector extends
BaseOutputConnector {
try {
- DBCollection mongoCollection =
mongoDatabase.getCollection(collection);
+ MongoCollection<Document> mongoCollection =
mongoDatabase.getCollection(collection);
- BasicDBObject query = new BasicDBObject();
- query.append("documentURI", documentURI);
+ Bson query = Filters.eq("documentURI", documentURI);
- WriteResult result = mongoCollection.remove(query);
- Logging.connectors.info("Number of documents deleted : " +
result.getN());
+ DeleteResult result = mongoCollection.deleteOne(query);
+ Logging.connectors.info("Number of documents deleted : " +
result.getDeletedCount());
- if (result.getN() > 0) {
+ if (result.getDeletedCount() > 0) {
resultDescription = DOCUMENT_DELETION_STATUS_ACCEPTED;
} else {
resultDescription = DOCUMENT_DELETION_STATUS_REJECTED;
diff --git
a/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/APISanityHSQLDBIT.java
b/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/APISanityHSQLDBIT.java
index e759ea39f..a66ceeb49 100644
---
a/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/APISanityHSQLDBIT.java
+++
b/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/APISanityHSQLDBIT.java
@@ -18,17 +18,22 @@
*/
package org.apache.manifoldcf.agents.output.mongodboutput.tests;
-import com.mongodb.*;
+import com.mongodb.MongoClientSettings;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
import org.apache.manifoldcf.agents.output.mongodboutput.MongodbOutputConfig;
import org.apache.manifoldcf.core.interfaces.Configuration;
import org.apache.manifoldcf.core.interfaces.ConfigurationNode;
import org.apache.manifoldcf.core.interfaces.ManifoldCFException;
import org.apache.manifoldcf.crawler.system.ManifoldCF;
+import org.bson.Document;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import java.net.UnknownHostException;
import java.util.Collections;
/**
@@ -37,26 +42,20 @@ import java.util.Collections;
public class APISanityHSQLDBIT extends BaseITHSQLDB {
private MongoClient client = null;
- private DB mongoDatabase = null;
- private DBCollection testCollection = null;
+ private MongoDatabase mongoDatabase = null;
+ private MongoCollection<Document> testCollection = null;
private MongoClient getMongodbClientSession() throws Exception {
-
- MongoClient testClient = null;
-
- try {
- testClient = new
MongoClient(BaseITSanityTestUtils.TARGET_HOST_VALUE,
Integer.parseInt(BaseITSanityTestUtils.TARGET_PORT_VALUE));
- mongoDatabase =
testClient.getDB(BaseITSanityTestUtils.TARGET_DATABASE__VALUE);
- } catch (UnknownHostException ex) {
- throw new ManifoldCFException("Mongodb: Default host is not found.
Is mongod process running?" + ex.getMessage(), ex);
- }
-
- return testClient;
+ String host = BaseITSanityTestUtils.TARGET_HOST_VALUE;
+ int port = Integer.parseInt(BaseITSanityTestUtils.TARGET_PORT_VALUE);
+ return MongoClients.create(MongoClientSettings.builder()
+ .applyToClusterSettings(b -> b.hosts(Collections.singletonList(new
ServerAddress(host, port))))
+ .build());
}
- private long queryTestContents(DBCollection testCollection) {
+ private long queryTestContents(MongoCollection<Document> testCollection) {
// deduct 1 for the document we created at the test area
- return testCollection.count() - 1;
+ return testCollection.countDocuments() - 1;
}
@Before
@@ -67,20 +66,22 @@ public class APISanityHSQLDBIT extends BaseITHSQLDB {
client = getMongodbClientSession();
//creating a test database named testDatabase
- mongoDatabase =
client.getDB(BaseITSanityTestUtils.TARGET_DATABASE__VALUE);
+ mongoDatabase =
client.getDatabase(BaseITSanityTestUtils.TARGET_DATABASE__VALUE);
//creating a test collection named testCollection
testCollection =
mongoDatabase.getCollection(BaseITSanityTestUtils.TARGET_COLLECTION_VALUE);
//create credentials
- final BasicDBObject createUserCommand = new
BasicDBObject("createUser",
BaseITSanityTestUtils.TARGET_USERNAME_VALUE).append("pwd",
BaseITSanityTestUtils.TARGET_PASSWORD_VALUE).append("roles",
- Collections.singletonList(new BasicDBObject("role",
"readWrite").append("db", BaseITSanityTestUtils.TARGET_DATABASE__VALUE)));
+ final Document createUserCommand = new Document("createUser",
BaseITSanityTestUtils.TARGET_USERNAME_VALUE)
+ .append("pwd", BaseITSanityTestUtils.TARGET_PASSWORD_VALUE)
+ .append("roles",
+ Collections.singletonList(new Document("role",
"readWrite").append("db", BaseITSanityTestUtils.TARGET_DATABASE__VALUE)));
- CommandResult result = mongoDatabase.command(createUserCommand);
+ mongoDatabase.runCommand(createUserCommand);
//create a document to be inserted
- BasicDBObject newDocument = new BasicDBObject();
+ Document newDocument = new Document();
newDocument.append("fileName", "fileName")
.append("creationDate", "creationDate")
.append("lastModificationDate", "lastModificationDate")
@@ -92,7 +93,7 @@ public class APISanityHSQLDBIT extends BaseITHSQLDB {
.append("sourcePath", "sourcePath");
//insert the test document so that the test collection and test
Database are created
- testCollection.insert(newDocument);
+ testCollection.insertOne(newDocument);
} catch (Exception e) {
e.printStackTrace();
throw e;
@@ -105,9 +106,13 @@ public class APISanityHSQLDBIT extends BaseITHSQLDB {
throws Exception {
if (mongoDatabase != null) {
- // deleting the dummyuser
- final BasicDBObject deleteUserCommand = new
BasicDBObject("dropUser", BaseITSanityTestUtils.TARGET_USERNAME_VALUE);
- CommandResult result = mongoDatabase.command(deleteUserCommand);
+ try {
+ // deleting the dummyuser
+ final Document deleteUserCommand = new Document("dropUser",
BaseITSanityTestUtils.TARGET_USERNAME_VALUE);
+ mongoDatabase.runCommand(deleteUserCommand);
+ } catch (Exception e) {
+ // ignore
+ }
// dropping the test Collection
if (testCollection != null) {
@@ -115,7 +120,11 @@ public class APISanityHSQLDBIT extends BaseITHSQLDB {
}
// dropping the test Database
- mongoDatabase.dropDatabase();
+ mongoDatabase.drop();
+ }
+ if (client != null) {
+ client.close();
+ client = null;
}
}
diff --git
a/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/BaseITHSQLDB.java
b/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/BaseITHSQLDB.java
index 28e6c0f49..a2eb26666 100644
---
a/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/BaseITHSQLDB.java
+++
b/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/BaseITHSQLDB.java
@@ -21,17 +21,8 @@ package
org.apache.manifoldcf.agents.output.mongodboutput.tests;
import org.junit.After;
import org.junit.Before;
-import de.flapdoodle.embed.mongo.config.Net;
-import de.flapdoodle.embed.mongo.distribution.Version;
-import de.flapdoodle.embed.mongo.transitions.ImmutableMongod;
-import de.flapdoodle.embed.mongo.transitions.Mongod;
-import de.flapdoodle.embed.mongo.transitions.RunningMongodProcess;
-import de.flapdoodle.os.CommonArchitecture;
-import de.flapdoodle.os.CommonOS;
-import de.flapdoodle.os.ImmutablePlatform;
-import de.flapdoodle.os.Platform;
-import de.flapdoodle.reverse.TransitionWalker;
-import de.flapdoodle.reverse.transitions.Start;
+import org.testcontainers.mongodb.MongoDBContainer;
+import org.testcontainers.utility.DockerImageName;
/**
* Base integration tests class for MongoDB tested against a CMIS repository
@@ -40,7 +31,7 @@ import de.flapdoodle.reverse.transitions.Start;
*/
public class BaseITHSQLDB extends
org.apache.manifoldcf.crawler.tests.BaseITHSQLDB {
- private TransitionWalker.ReachedState<RunningMongodProcess>
mongodProcess;
+ protected static MongoDBContainer mongoDBContainer;
protected String[] getConnectorNames() {
return new String[] { "CMIS" };
@@ -62,27 +53,19 @@ public class BaseITHSQLDB extends
org.apache.manifoldcf.crawler.tests.BaseITHSQL
@Before
public void setUpMongoDB() throws Exception {
-
- String bindIp = "localhost";
- int port = 27017;
- ImmutableMongod.Builder mongodBuilder = Mongod.builder()
-
.net(Start.to(Net.class).initializedWith(Net.of(bindIp, port, false)));
-
- Platform detected = Platform.detect(CommonOS.list());
- if (detected.operatingSystem() == CommonOS.OS_X &&
detected.architecture() == CommonArchitecture.ARM_64) {
- Platform x86Platform =
ImmutablePlatform.builder().from(detected)
- .architecture(CommonArchitecture.X86_64)
- .build();
-
mongodBuilder.platform(Start.to(Platform.class).initializedWith(x86Platform));
+ if (mongoDBContainer == null) {
+ mongoDBContainer = new
MongoDBContainer(DockerImageName.parse("mongo:9.0.2"));
+ mongoDBContainer.start();
}
-
- mongodProcess = mongodBuilder.build().start(Version.Main.V4_0);
+ BaseITSanityTestUtils.TARGET_HOST_VALUE =
mongoDBContainer.getHost();
+ BaseITSanityTestUtils.TARGET_PORT_VALUE =
String.valueOf(mongoDBContainer.getFirstMappedPort());
}
@After
public void cleanUpMongoDB() throws Exception {
- if (mongodProcess != null) {
- mongodProcess.close();
+ if (mongoDBContainer != null) {
+ mongoDBContainer.stop();
+ mongoDBContainer = null;
}
}
diff --git
a/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/BaseITSanityTestUtils.java
b/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/BaseITSanityTestUtils.java
index b7af0d75b..dd6df3c81 100644
---
a/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/BaseITSanityTestUtils.java
+++
b/connectors/mongodb/connector/src/test/java/org/apache/manifoldcf/agents/output/mongodboutput/tests/BaseITSanityTestUtils.java
@@ -30,8 +30,8 @@ public class BaseITSanityTestUtils {
//Test values for the source repository
public static final String TARGET_USERNAME_VALUE = "dummyuser";
public static final String TARGET_PASSWORD_VALUE = "dummysecrect";
- public static final String TARGET_HOST_VALUE = "localhost";
- public static final String TARGET_PORT_VALUE = "27017";
+ public static String TARGET_HOST_VALUE = "localhost";
+ public static String TARGET_PORT_VALUE = "27017";
public static final String TARGET_DATABASE__VALUE = "testDatabase";
public static final String TARGET_COLLECTION_VALUE = "testCollection";
diff --git a/connectors/mongodb/pom.xml b/connectors/mongodb/pom.xml
index 3a0591680..27674f7f2 100644
--- a/connectors/mongodb/pom.xml
+++ b/connectors/mongodb/pom.xml
@@ -29,7 +29,6 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <mongo.java.driver.version>2.14.3</mongo.java.driver.version>
</properties>
<developers>
@@ -74,8 +73,7 @@
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
- <artifactId>mongo-java-driver</artifactId>
- <version>${mongo.java.driver.version}</version>
+ <artifactId>mongodb-driver-sync</artifactId>
</dependency>
<!-- Test dependencies -->
@@ -87,9 +85,8 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>de.flapdoodle.embed</groupId>
- <artifactId>de.flapdoodle.embed.mongo</artifactId>
- <version>4.33.0</version>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-mongodb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
diff --git a/connectors/mongodb/test-materials/README.txt
b/connectors/mongodb/test-materials/README.txt
index 4be9d6c1b..bf08d6249 100644
--- a/connectors/mongodb/test-materials/README.txt
+++ b/connectors/mongodb/test-materials/README.txt
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-To test this connector, download the Embedded MongoDB dependencies using "ant
download-dependencies"
+To test this connector, download the Testcontainers dependencies using "ant
download-dependencies"
or the global "ant make-deps" target, and use the "ant test" target for the
standard ant build.
-You can read more about the process on the "how-to-build-and-deploy.html"
documentation page.
\ No newline at end of file
+The integration tests start MongoDB 9.0.2 as a Docker container, so a running
Docker daemon is required.
+You can read more about the process on the "how-to-build-and-deploy.html"
documentation page.