You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2017/12/05 12:41:24 UTC

svn commit: r1817183 [2/2] - in /jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document: ./ mongo/ rdb/

Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java?rev=1817183&r1=1817182&r2=1817183&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java Tue Dec  5 12:41:23 2017
@@ -23,8 +23,10 @@ import static com.google.common.collect.
 import static java.util.Collections.emptyList;
 import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly;
 import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toLong;
-import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_MEMORY_CACHE_SIZE;
+import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder.DEFAULT_MEMORY_CACHE_SIZE;
 import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.MODIFIED_IN_SECS_RESOLUTION;
+import static org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentNodeStoreBuilder.newMongoDocumentNodeStoreBuilder;
+import static org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentNodeStoreBuilder.newRDBDocumentNodeStoreBuilder;
 import static org.apache.jackrabbit.oak.spi.blob.osgi.SplitBlobStoreService.ONLY_STANDALONE_TARGET;
 import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
 import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.scheduleWithFixedDelay;
@@ -63,6 +65,8 @@ import org.apache.jackrabbit.oak.api.jmx
 import org.apache.jackrabbit.oak.api.jmx.PersistentCacheStatsMBean;
 import org.apache.jackrabbit.oak.cache.CacheStats;
 import org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
+import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentNodeStoreBuilder;
+import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDocumentNodeStoreBuilder;
 import org.apache.jackrabbit.oak.plugins.document.util.Utils;
 import org.apache.jackrabbit.oak.spi.commit.ObserverTracker;
 import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
@@ -248,83 +252,31 @@ public class DocumentNodeStoreService {
     }
 
     private void registerNodeStore() throws IOException {
-        String persistentCache = resolvePath(config.persistentCache(), DEFAULT_PERSISTENT_CACHE);
-        String journalCache = resolvePath(config.journalCache(), DEFAULT_JOURNAL_CACHE);
-        DocumentMK.Builder mkBuilder =
-                new DocumentMK.Builder().
-                setStatisticsProvider(statisticsProvider).
-                memoryCacheSize(config.cache() * MB).
-                memoryCacheDistribution(
-                        config.nodeCachePercentage(),
-                        config.prevDocCachePercentage(),
-                        config.childrenCachePercentage(),
-                        config.diffCachePercentage()).
-                setCacheSegmentCount(config.cacheSegmentCount()).
-                setCacheStackMoveDistance(config.cacheStackMoveDistance()).
-                setBundlingDisabled(config.bundlingDisabled()).
-                setJournalPropertyHandlerFactory(journalPropertyHandlerFactory).
-                setLeaseCheck(!ClusterNodeInfo.DEFAULT_LEASE_CHECK_DISABLED /* OAK-2739: enabled by default */).
-                setLeaseFailureHandler(new LeaseFailureHandler() {
-                    
-                    @Override
-                    public void handleLeaseFailure() {
-                        try {
-                            // plan A: try stopping oak-core
-                            log.error("handleLeaseFailure: stopping oak-core...");
-                            Bundle bundle = context.getBundleContext().getBundle();
-                            bundle.stop(Bundle.STOP_TRANSIENT);
-                            log.error("handleLeaseFailure: stopped oak-core.");
-                            // plan A worked, perfect!
-                        } catch (BundleException e) {
-                            log.error("handleLeaseFailure: exception while stopping oak-core: "+e, e);
-                            // plan B: stop only DocumentNodeStoreService (to stop the background threads)
-                            log.error("handleLeaseFailure: stopping DocumentNodeStoreService...");
-                            context.disableComponent(DocumentNodeStoreService.class.getName());
-                            log.error("handleLeaseFailure: stopped DocumentNodeStoreService");
-                            // plan B succeeded.
-                        }
-                    }
-                }).
-                setPrefetchExternalChanges(config.prefetchExternalChanges()).
-                setUpdateLimit(config.updateLimit()).
-                setJournalGCMaxAge(config.journalGCMaxAge()).
-                setNodeCachePredicate(createCachePredicate());
-
-        if (!Strings.isNullOrEmpty(persistentCache)) {
-            mkBuilder.setPersistentCache(persistentCache);
-        }
-        if (!Strings.isNullOrEmpty(journalCache)) {
-            mkBuilder.setJournalCache(journalCache);
-        }
-
-        boolean wrappingCustomBlobStore = customBlobStore && blobStore instanceof BlobStoreWrapper;
-
-        //Set blobstore before setting the DB
-        if (customBlobStore && !wrappingCustomBlobStore) {
-            checkNotNull(blobStore, "Use of custom BlobStore enabled via  [%s] but blobStore reference not " +
-                    "initialized", CUSTOM_BLOB_STORE);
-            mkBuilder.setBlobStore(blobStore);
-        }
-
+        DocumentNodeStoreBuilder mkBuilder;
         if (documentStoreType == DocumentStoreType.RDB) {
+            RDBDocumentNodeStoreBuilder builder = newRDBDocumentNodeStoreBuilder();
+            configureBuilder(builder);
             checkNotNull(dataSource, "DataStore type set [%s] but DataSource reference not initialized", PROP_DS_TYPE);
             if (!customBlobStore) {
                 checkNotNull(blobDataSource, "DataStore type set [%s] but BlobDataSource reference not initialized", PROP_DS_TYPE);
-                mkBuilder.setRDBConnection(dataSource, blobDataSource);
+                builder.setRDBConnection(dataSource, blobDataSource);
                 log.info("Connected to datasources {} {}", dataSource, blobDataSource);
             } else {
                 if (blobDataSource != null && blobDataSource != dataSource) {
                     log.info("Ignoring blobDataSource {} as custom blob store takes precedence.", blobDataSource);
                 }
-                mkBuilder.setRDBConnection(dataSource);
+                builder.setRDBConnection(dataSource);
                 log.info("Connected to datasource {}", dataSource);
             }
+            mkBuilder = builder;
         } else {
             String uri = config.mongouri();
             String db = config.db();
             boolean soKeepAlive = config.socketKeepAlive();
 
             MongoClientURI mongoURI = new MongoClientURI(uri);
+            String persistentCache = resolvePath(config.persistentCache(), DEFAULT_PERSISTENT_CACHE);
+            String journalCache = resolvePath(config.journalCache(), DEFAULT_JOURNAL_CACHE);
 
             if (log.isInfoEnabled()) {
                 // Take care around not logging the uri directly as it
@@ -336,9 +288,12 @@ public class DocumentNodeStoreService {
                 log.info("Mongo Connection details {}", MongoConnection.toString(mongoURI.getOptions()));
             }
 
-            mkBuilder.setMaxReplicationLag(config.maxReplicationLagInSecs(), TimeUnit.SECONDS);
-            mkBuilder.setSocketKeepAlive(soKeepAlive);
-            mkBuilder.setMongoDB(uri, db, config.blobCacheSize());
+            MongoDocumentNodeStoreBuilder builder = newMongoDocumentNodeStoreBuilder();
+            configureBuilder(builder);
+            builder.setMaxReplicationLag(config.maxReplicationLagInSecs(), TimeUnit.SECONDS);
+            builder.setSocketKeepAlive(soKeepAlive);
+            builder.setMongoDB(uri, db, config.blobCacheSize());
+            mkBuilder = builder;
 
             log.info("Connected to database '{}'", db);
         }
@@ -351,7 +306,7 @@ public class DocumentNodeStoreService {
         }
 
         //Set wrapping blob store after setting the DB
-        if (wrappingCustomBlobStore) {
+        if (isWrappingCustomBlobStore()) {
             ((BlobStoreWrapper) blobStore).setBlobStore(mkBuilder.getBlobStore());
             mkBuilder.setBlobStore(blobStore);
         }
@@ -373,7 +328,7 @@ public class DocumentNodeStoreService {
         mkBuilder.setGCMonitor(new DelegatingGCMonitor(
                 newArrayList(gcMonitor, loggingGCMonitor)));
 
-        nodeStore = mkBuilder.getNodeStore();
+        nodeStore = mkBuilder.build();
 
         // ensure a clusterId is initialized 
         // and expose it as 'oak.clusterid' repository descriptor
@@ -463,6 +418,66 @@ public class DocumentNodeStoreService {
             nodeStore, props);
     }
 
+    private void configureBuilder(DocumentNodeStoreBuilder builder) {
+        String persistentCache = resolvePath(config.persistentCache(), DEFAULT_PERSISTENT_CACHE);
+        String journalCache = resolvePath(config.journalCache(), DEFAULT_JOURNAL_CACHE);
+        builder.setStatisticsProvider(statisticsProvider).
+                memoryCacheSize(config.cache() * MB).
+                memoryCacheDistribution(
+                        config.nodeCachePercentage(),
+                        config.prevDocCachePercentage(),
+                        config.childrenCachePercentage(),
+                        config.diffCachePercentage()).
+                setCacheSegmentCount(config.cacheSegmentCount()).
+                setCacheStackMoveDistance(config.cacheStackMoveDistance()).
+                setBundlingDisabled(config.bundlingDisabled()).
+                setJournalPropertyHandlerFactory(journalPropertyHandlerFactory).
+                setLeaseCheck(!ClusterNodeInfo.DEFAULT_LEASE_CHECK_DISABLED /* OAK-2739: enabled by default */).
+                setLeaseFailureHandler(new LeaseFailureHandler() {
+
+                    @Override
+                    public void handleLeaseFailure() {
+                        try {
+                            // plan A: try stopping oak-core
+                            log.error("handleLeaseFailure: stopping oak-core...");
+                            Bundle bundle = context.getBundleContext().getBundle();
+                            bundle.stop(Bundle.STOP_TRANSIENT);
+                            log.error("handleLeaseFailure: stopped oak-core.");
+                            // plan A worked, perfect!
+                        } catch (BundleException e) {
+                            log.error("handleLeaseFailure: exception while stopping oak-core: "+e, e);
+                            // plan B: stop only DocumentNodeStoreService (to stop the background threads)
+                            log.error("handleLeaseFailure: stopping DocumentNodeStoreService...");
+                            context.disableComponent(DocumentNodeStoreService.class.getName());
+                            log.error("handleLeaseFailure: stopped DocumentNodeStoreService");
+                            // plan B succeeded.
+                        }
+                    }
+                }).
+                setPrefetchExternalChanges(config.prefetchExternalChanges()).
+                setUpdateLimit(config.updateLimit()).
+                setJournalGCMaxAge(config.journalGCMaxAge()).
+                setNodeCachePredicate(createCachePredicate());
+
+        if (!Strings.isNullOrEmpty(persistentCache)) {
+            builder.setPersistentCache(persistentCache);
+        }
+        if (!Strings.isNullOrEmpty(journalCache)) {
+            builder.setJournalCache(journalCache);
+        }
+
+        //Set blobstore before setting the document store
+        if (customBlobStore && !isWrappingCustomBlobStore()) {
+            checkNotNull(blobStore, "Use of custom BlobStore enabled via  [%s] but blobStore reference not " +
+                    "initialized", CUSTOM_BLOB_STORE);
+            builder.setBlobStore(blobStore);
+        }
+    }
+
+    private boolean isWrappingCustomBlobStore() {
+        return customBlobStore && blobStore instanceof BlobStoreWrapper;
+    }
+
     private Predicate<String> createCachePredicate() {
         if (config.persistentCacheIncludes().length == 0) {
             return Predicates.alwaysTrue();
@@ -671,7 +686,7 @@ public class DocumentNodeStoreService {
         }
     }
 
-    private void registerJMXBeans(final DocumentNodeStore store, DocumentMK.Builder mkBuilder) throws
+    private void registerJMXBeans(final DocumentNodeStore store, DocumentNodeStoreBuilder mkBuilder) throws
             IOException {
         registerCacheStatsMBean(store.getNodeCacheStats());
         registerCacheStatsMBean(store.getNodeChildrenCacheStats());

Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java?rev=1817183&r1=1817182&r2=1817183&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/LocalDiffCache.java Tue Dec  5 12:41:23 2017
@@ -49,7 +49,7 @@ public class LocalDiffCache extends Diff
     private final Cache<RevisionsKey, Diff> diffCache;
     private final CacheStats diffCacheStats;
 
-    LocalDiffCache(DocumentMK.Builder builder) {
+    LocalDiffCache(DocumentNodeStoreBuilder builder) {
         this.diffCache = builder.buildLocalDiffCache();
         this.diffCacheStats = new CacheStats(diffCache,
                 "Document-LocalDiff",

Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java?rev=1817183&r1=1817182&r2=1817183&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/MemoryDiffCache.java Tue Dec  5 12:41:23 2017
@@ -59,7 +59,7 @@ public class MemoryDiffCache extends Dif
     protected final CacheStats diffCacheStats;
 
 
-    protected MemoryDiffCache(DocumentMK.Builder builder) {
+    protected MemoryDiffCache(DocumentNodeStoreBuilder builder) {
         diffCache = builder.buildMemoryDiffCache();
         diffCacheStats = new CacheStats(diffCache, "Document-MemoryDiff",
                 builder.getWeigher(), builder.getMemoryDiffCacheSize());

Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/TieredDiffCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/TieredDiffCache.java?rev=1817183&r1=1817182&r2=1817183&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/TieredDiffCache.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/TieredDiffCache.java Tue Dec  5 12:41:23 2017
@@ -32,7 +32,7 @@ class TieredDiffCache extends DiffCache
     private final DiffCache localCache;
     private final DiffCache memoryCache;
 
-    TieredDiffCache(DocumentMK.Builder builder) {
+    TieredDiffCache(DocumentNodeStoreBuilder builder) {
         this.localCache = new LocalDiffCache(builder);
         this.memoryCache = new MemoryDiffCache(builder);
     }

Added: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilder.java?rev=1817183&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilder.java (added)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilder.java Tue Dec  5 12:41:23 2017
@@ -0,0 +1,222 @@
+/*
+ * 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.jackrabbit.oak.plugins.document.mongo;
+
+import java.net.UnknownHostException;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nonnull;
+
+import com.mongodb.DB;
+import com.mongodb.MongoClientOptions;
+import com.mongodb.ReadConcernLevel;
+
+import org.apache.jackrabbit.oak.plugins.blob.ReferencedBlob;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder;
+import org.apache.jackrabbit.oak.plugins.document.DocumentStore;
+import org.apache.jackrabbit.oak.plugins.document.MissingLastRevSeeker;
+import org.apache.jackrabbit.oak.plugins.document.VersionGCSupport;
+import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
+import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.common.base.Suppliers.memoize;
+import static org.apache.jackrabbit.oak.plugins.document.util.MongoConnection.readConcernLevel;
+
+/**
+ * A builder for a {@link DocumentNodeStore} backed by MongoDB.
+ */
+public class MongoDocumentNodeStoreBuilder<T extends MongoDocumentNodeStoreBuilder<T>>
+        extends DocumentNodeStoreBuilder<T> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MongoDocumentNodeStoreBuilder.class);
+
+    private String mongoUri;
+    private boolean socketKeepAlive;
+    private MongoStatus mongoStatus;
+    private long maxReplicationLagMillis = TimeUnit.HOURS.toMillis(6);
+
+    /**
+     * @return a new {@link MongoDocumentNodeStoreBuilder}.
+     */
+    public static MongoDocumentNodeStoreBuilder newMongoDocumentNodeStoreBuilder() {
+        return new MongoDocumentNodeStoreBuilder();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected T thisBuilder() {
+        return (T) this;
+    }
+
+    /**
+     * Uses the given information to connect to to MongoDB as backend
+     * storage for the DocumentNodeStore. The write concern is either
+     * taken from the URI or determined automatically based on the MongoDB
+     * setup. When running on a replica set without explicit write concern
+     * in the URI, the write concern will be {@code MAJORITY}, otherwise
+     * {@code ACKNOWLEDGED}.
+     *
+     * @param uri a MongoDB URI.
+     * @param name the name of the database to connect to. This overrides
+     *             any database name given in the {@code uri}.
+     * @param blobCacheSizeMB the blob cache size in MB.
+     * @return this
+     * @throws UnknownHostException if one of the hosts given in the URI
+     *          is unknown.
+     */
+    public T setMongoDB(@Nonnull String uri,
+                        @Nonnull String name,
+                        int blobCacheSizeMB)
+            throws UnknownHostException {
+        this.mongoUri = uri;
+
+        MongoClientOptions.Builder options = MongoConnection.getDefaultBuilder();
+        options.socketKeepAlive(socketKeepAlive);
+        DB db = new MongoConnection(uri, options).getDB(name);
+        MongoStatus status = new MongoStatus(db);
+        if (!MongoConnection.hasWriteConcern(uri)) {
+            db.setWriteConcern(MongoConnection.getDefaultWriteConcern(db));
+        }
+        if (status.isMajorityReadConcernSupported() && status.isMajorityReadConcernEnabled() && !MongoConnection.hasReadConcern(uri)) {
+            db.setReadConcern(MongoConnection.getDefaultReadConcern(db));
+        }
+        setMongoDB(db, status, blobCacheSizeMB);
+        return thisBuilder();
+    }
+
+    /**
+     * Use the given MongoDB as backend storage for the DocumentNodeStore.
+     *
+     * @param db the MongoDB connection
+     * @return this
+     */
+    public T setMongoDB(@Nonnull DB db,
+                        int blobCacheSizeMB) {
+        return setMongoDB(db, new MongoStatus(db), blobCacheSizeMB);
+    }
+
+    /**
+     * Use the given MongoDB as backend storage for the DocumentNodeStore.
+     *
+     * @param db the MongoDB connection
+     * @return this
+     */
+    public T setMongoDB(@Nonnull DB db) {
+        return setMongoDB(db, 16);
+    }
+
+    /**
+     * Enables the socket keep-alive option for MongoDB. The default is
+     * disabled.
+     *
+     * @param enable whether to enable it.
+     * @return this
+     */
+    public T setSocketKeepAlive(boolean enable) {
+        this.socketKeepAlive = enable;
+        return thisBuilder();
+    }
+
+    public T setMaxReplicationLag(long duration, TimeUnit unit){
+        maxReplicationLagMillis = unit.toMillis(duration);
+        return thisBuilder();
+    }
+
+    public VersionGCSupport createVersionGCSupport() {
+        DocumentStore store = getDocumentStore();
+        if (store instanceof MongoDocumentStore) {
+            return new MongoVersionGCSupport((MongoDocumentStore) store);
+        } else {
+            return super.createVersionGCSupport();
+        }
+    }
+
+    public Iterable<ReferencedBlob> createReferencedBlobs(DocumentNodeStore ns) {
+        final DocumentStore store = getDocumentStore();
+        if (store instanceof MongoDocumentStore) {
+            return () -> new MongoBlobReferenceIterator(ns, (MongoDocumentStore) store);
+        } else {
+            return super.createReferencedBlobs(ns);
+        }
+    }
+
+    public MissingLastRevSeeker createMissingLastRevSeeker() {
+        final DocumentStore store = getDocumentStore();
+        if (store instanceof MongoDocumentStore) {
+            return new MongoMissingLastRevSeeker((MongoDocumentStore) store, getClock());
+        } else {
+            return super.createMissingLastRevSeeker();
+        }
+    }
+
+    /**
+     * Returns the Mongo URI used in the {@link #setMongoDB(String, String, int)} method.
+     *
+     * @return the Mongo URI or null if the {@link #setMongoDB(String, String, int)} method hasn't
+     * been called.
+     */
+    String getMongoUri() {
+        return mongoUri;
+    }
+
+    /**
+     * Returns the status of the Mongo server configured in the {@link #setMongoDB(String, String, int)} method.
+     *
+     * @return the status or null if the {@link #setMongoDB(String, String, int)} method hasn't
+     * been called.
+     */
+    MongoStatus getMongoStatus() {
+        return mongoStatus;
+    }
+
+    long getMaxReplicationLagMillis() {
+        return maxReplicationLagMillis;
+    }
+
+    private T setMongoDB(@Nonnull DB db,
+                         MongoStatus status,
+                         int blobCacheSizeMB) {
+        if (!MongoConnection.hasSufficientWriteConcern(db)) {
+            LOG.warn("Insufficient write concern: " + db.getWriteConcern()
+                    + " At least " + MongoConnection.getDefaultWriteConcern(db) + " is recommended.");
+        }
+        if (status.isMajorityReadConcernSupported() && !status.isMajorityReadConcernEnabled()) {
+            LOG.warn("The read concern should be enabled on mongod using --enableMajorityReadConcern");
+        } else if (status.isMajorityReadConcernSupported() && !MongoConnection.hasSufficientReadConcern(db)) {
+            ReadConcernLevel currentLevel = readConcernLevel(db.getReadConcern());
+            ReadConcernLevel recommendedLevel = readConcernLevel(MongoConnection.getDefaultReadConcern(db));
+            if (currentLevel == null) {
+                LOG.warn("Read concern hasn't been set. At least " + recommendedLevel + " is recommended.");
+            } else {
+                LOG.warn("Insufficient read concern: " + currentLevel + ". At least " + recommendedLevel + " is recommended.");
+            }
+        }
+
+        this.mongoStatus = status;
+        this.documentStoreSupplier = memoize(() -> new MongoDocumentStore(
+                db, MongoDocumentNodeStoreBuilder.this));
+
+        if (this.blobStore == null) {
+            GarbageCollectableBlobStore s = new MongoBlobStore(db, blobCacheSizeMB * 1024 * 1024L);
+            setGCBlobStore(s);
+        }
+        return thisBuilder();
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentNodeStoreBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java?rev=1817183&r1=1817182&r2=1817183&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java Tue Dec  5 12:41:23 2017
@@ -54,7 +54,6 @@ import org.apache.jackrabbit.oak.cache.C
 import org.apache.jackrabbit.oak.cache.CacheValue;
 import org.apache.jackrabbit.oak.plugins.document.Collection;
 import org.apache.jackrabbit.oak.plugins.document.Document;
-import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
 import org.apache.jackrabbit.oak.plugins.document.DocumentStore;
 import org.apache.jackrabbit.oak.plugins.document.DocumentStoreException;
 import org.apache.jackrabbit.oak.plugins.document.DocumentStoreStatsCollector;
@@ -231,7 +230,7 @@ public class MongoDocumentStore implemen
 
     private static final Key KEY_MODIFIED = new Key(MODIFIED_IN_SECS, null);
 
-    public MongoDocumentStore(DB db, DocumentMK.Builder builder) {
+    public MongoDocumentStore(DB db, MongoDocumentNodeStoreBuilder builder) {
         MongoStatus mongoStatus = builder.getMongoStatus();
         if (mongoStatus == null) {
             mongoStatus = new MongoStatus(db);

Added: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java?rev=1817183&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java (added)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java Tue Dec  5 12:41:23 2017
@@ -0,0 +1,107 @@
+/*
+ * 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.jackrabbit.oak.plugins.document.rdb;
+
+import javax.sql.DataSource;
+
+import org.apache.jackrabbit.oak.plugins.blob.ReferencedBlob;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder;
+import org.apache.jackrabbit.oak.plugins.document.DocumentStore;
+import org.apache.jackrabbit.oak.plugins.document.VersionGCSupport;
+import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
+
+import static com.google.common.base.Suppliers.ofInstance;
+
+/**
+ * A builder for a {@link DocumentNodeStore} backed by a relational database.
+ */
+public class RDBDocumentNodeStoreBuilder<T extends RDBDocumentNodeStoreBuilder<T>>
+        extends DocumentNodeStoreBuilder<T> {
+
+    /**
+     * @return a new {@link RDBDocumentNodeStoreBuilder}.
+     */
+    public static RDBDocumentNodeStoreBuilder newRDBDocumentNodeStoreBuilder() {
+        return new RDBDocumentNodeStoreBuilder();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected T thisBuilder() {
+        return (T) this;
+    }
+
+    /**
+     * Sets a {@link DataSource} to use for the RDB document and blob
+     * stores.
+     *
+     * @return this
+     */
+    public T setRDBConnection(DataSource ds) {
+        setRDBConnection(ds, new RDBOptions());
+        return thisBuilder();
+    }
+
+    /**
+     * Sets a {@link DataSource} to use for the RDB document and blob
+     * stores, including {@link RDBOptions}.
+     *
+     * @return this
+     */
+    public T setRDBConnection(DataSource ds, RDBOptions options) {
+        this.documentStoreSupplier = ofInstance(new RDBDocumentStore(ds, this, options));
+        if(blobStore == null) {
+            GarbageCollectableBlobStore s = new RDBBlobStore(ds, options);
+            setGCBlobStore(s);
+        }
+        return thisBuilder();
+    }
+
+    /**
+     * Sets a {@link DataSource}s to use for the RDB document and blob
+     * stores.
+     *
+     * @return this
+     */
+    public T setRDBConnection(DataSource documentStoreDataSource, DataSource blobStoreDataSource) {
+        this.documentStoreSupplier = ofInstance(new RDBDocumentStore(documentStoreDataSource, this));
+        if(blobStore == null) {
+            GarbageCollectableBlobStore s = new RDBBlobStore(blobStoreDataSource);
+            setGCBlobStore(s);
+        }
+        return thisBuilder();
+    }
+
+    public VersionGCSupport createVersionGCSupport() {
+        DocumentStore store = getDocumentStore();
+        if (store instanceof RDBDocumentStore) {
+            return new RDBVersionGCSupport((RDBDocumentStore) store);
+        } else {
+            return super.createVersionGCSupport();
+        }
+    }
+
+    public Iterable<ReferencedBlob> createReferencedBlobs(DocumentNodeStore ns) {
+        final DocumentStore store = getDocumentStore();
+        if (store instanceof RDBDocumentStore) {
+            return () -> new RDBBlobReferenceIterator(ns, (RDBDocumentStore) store);
+        } else {
+            return super.createReferencedBlobs(ns);
+        }
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentNodeStoreBuilder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java?rev=1817183&r1=1817182&r2=1817183&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java (original)
+++ jackrabbit/oak/trunk/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java Tue Dec  5 12:41:23 2017
@@ -66,7 +66,7 @@ import org.apache.jackrabbit.oak.cache.C
 import org.apache.jackrabbit.oak.cache.CacheValue;
 import org.apache.jackrabbit.oak.plugins.document.Collection;
 import org.apache.jackrabbit.oak.plugins.document.Document;
-import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder;
 import org.apache.jackrabbit.oak.plugins.document.DocumentStore;
 import org.apache.jackrabbit.oak.plugins.document.DocumentStoreException;
 import org.apache.jackrabbit.oak.plugins.document.DocumentStoreStatsCollector;
@@ -233,9 +233,9 @@ public class RDBDocumentStore implements
 
     /**
      * Creates a {@linkplain RDBDocumentStore} instance using the provided
-     * {@link DataSource}, {@link DocumentMK.Builder}, and {@link RDBOptions}.
+     * {@link DataSource}, {@link DocumentNodeStoreBuilder}, and {@link RDBOptions}.
      */
-    public RDBDocumentStore(DataSource ds, DocumentMK.Builder builder, RDBOptions options) {
+    public RDBDocumentStore(DataSource ds, DocumentNodeStoreBuilder builder, RDBOptions options) {
         try {
             initialize(ds, builder, options);
         } catch (Exception ex) {
@@ -245,10 +245,10 @@ public class RDBDocumentStore implements
 
     /**
      * Creates a {@linkplain RDBDocumentStore} instance using the provided
-     * {@link DataSource}, {@link DocumentMK.Builder}, and default
+     * {@link DataSource}, {@link DocumentNodeStoreBuilder}, and default
      * {@link RDBOptions}.
      */
-    public RDBDocumentStore(DataSource ds, DocumentMK.Builder builder) {
+    public RDBDocumentStore(DataSource ds, DocumentNodeStoreBuilder builder) {
         this(ds, builder, new RDBOptions());
     }
 
@@ -829,7 +829,7 @@ public class RDBDocumentStore implements
 
     private final RDBDocumentSerializer ser = new RDBDocumentSerializer(this);
 
-    private void initialize(DataSource ds, DocumentMK.Builder builder, RDBOptions options) throws Exception {
+    private void initialize(DataSource ds, DocumentNodeStoreBuilder builder, RDBOptions options) throws Exception {
         this.stats = builder.getDocumentStoreStatsCollector();
         this.tableMeta.put(Collection.NODES,
                 new RDBTableMetaData(createTableName(options.getTablePrefix(), TABLEMAP.get(Collection.NODES))));