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 fr...@apache.org on 2018/07/13 13:23:24 UTC

svn commit: r1835837 [1/3] - in /jackrabbit/oak/trunk: oak-it/src/test/java/org/apache/jackrabbit/oak/segment/ oak-segment-tar/ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/seg...

Author: frm
Date: Fri Jul 13 13:23:24 2018
New Revision: 1835837

URL: http://svn.apache.org/viewvc?rev=1835837&view=rev
Log:
OAK-6770 - Convert oak-segment-tar to OSGi R6 annotations

Added:
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java   (with props)
Removed:
    jackrabbit/oak/trunk/oak-segment-tar/src/main/resources/OSGI-INF/l10n/metatype.properties
Modified:
    jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCachingDataStoreStatsTest.java
    jackrabbit/oak/trunk/oak-segment-tar/pom.xml
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitorService.java
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceDeprecationError.java
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceDeprecationError.java
    jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java
    jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java
    jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/ComponentDescriptor.java
    jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreFactoryTest.java
    jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreMonitorServiceTest.java
    jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceTest.java
    jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceTest.java

Modified: jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCachingDataStoreStatsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCachingDataStoreStatsTest.java?rev=1835837&r1=1835836&r2=1835837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCachingDataStoreStatsTest.java (original)
+++ jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/segment/SegmentCachingDataStoreStatsTest.java Fri Jul 13 13:23:24 2018
@@ -18,6 +18,14 @@
  */
 package org.apache.jackrabbit.oak.segment;
 
+import static com.google.common.collect.Maps.newHashMap;
+import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.CUSTOM_BLOB_STORE;
+import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.REPOSITORY_HOME_DIRECTORY;
+import static org.apache.sling.testing.mock.osgi.MockOsgi.deactivate;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+
 import java.io.File;
 import java.util.Map;
 
@@ -30,21 +38,12 @@ import org.apache.jackrabbit.oak.stats.S
 import org.apache.sling.testing.mock.osgi.ReferenceViolationException;
 import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
 import org.osgi.framework.ServiceRegistration;
 
-import static com.google.common.collect.Maps.newHashMap;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.CUSTOM_BLOB_STORE;
-import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.REPOSITORY_HOME_DIRECTORY;
-import static org.apache.sling.testing.mock.osgi.MockOsgi.deactivate;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.mock;
-
 /**
  * Tests the registration of the {@link ConsolidatedDataStoreCacheStatsMBean}.
  */

Modified: jackrabbit/oak/trunk/oak-segment-tar/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/pom.xml?rev=1835837&r1=1835836&r2=1835837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/pom.xml Fri Jul 13 13:23:24 2018
@@ -253,8 +253,13 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.felix</groupId>
-            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.metatype.annotations</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>

Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java?rev=1835837&r1=1835836&r2=1835837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java Fri Jul 13 13:23:24 2018
@@ -16,40 +16,60 @@
  */
 package org.apache.jackrabbit.oak.segment;
 
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly;
 import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework;
+import static org.apache.jackrabbit.oak.segment.CachingSegmentReader.DEFAULT_STRING_CACHE_MB;
+import static org.apache.jackrabbit.oak.segment.CachingSegmentReader.DEFAULT_TEMPLATE_CACHE_MB;
+import static org.apache.jackrabbit.oak.segment.SegmentCache.DEFAULT_SEGMENT_CACHE_MB;
+import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_NODE_CACHE_SIZE_OSGi;
+import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_STRING_CACHE_SIZE_OSGi;
+import static org.apache.jackrabbit.oak.segment.WriterCacheManager.DEFAULT_TEMPLATE_CACHE_SIZE_OSGi;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.DISABLE_ESTIMATION_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.FORCE_TIMEOUT_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.GC_PROGRESS_LOG_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.MEMORY_THRESHOLD_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.PAUSE_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETAINED_GENERATIONS_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETRY_COUNT_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.SIZE_DELTA_ESTIMATION_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.DEFAULT_MAX_FILE_SIZE;
 import static org.apache.jackrabbit.oak.spi.blob.osgi.SplitBlobStoreService.ONLY_STANDALONE_TARGET;
 
 import java.io.Closeable;
+import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
 import com.google.common.io.Closer;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.ConfigurationPolicy;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.ReferencePolicy;
-import org.apache.felix.scr.annotations.ReferencePolicyOption;
 import org.apache.jackrabbit.api.stats.RepositoryStatistics;
 import org.apache.jackrabbit.api.stats.TimeSeries;
-import org.apache.jackrabbit.oak.commons.IOUtils;
 import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
 import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence;
 import org.apache.jackrabbit.oak.spi.blob.BlobStore;
-import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.spi.state.NodeStoreProvider;
 import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
 import org.apache.jackrabbit.oak.stats.CounterStats;
 import org.apache.jackrabbit.oak.stats.HistogramStats;
 import org.apache.jackrabbit.oak.stats.MeterStats;
 import org.apache.jackrabbit.oak.stats.StatisticsProvider;
 import org.apache.jackrabbit.oak.stats.StatsOptions;
 import org.apache.jackrabbit.oak.stats.TimerStats;
+import org.osgi.framework.BundleContext;
 import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,111 +78,272 @@ import org.slf4j.LoggerFactory;
  * <p>
  * The different secondaries are distinguished by their role attribute.
  */
-@Component(policy = ConfigurationPolicy.REQUIRE,
-        name="org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory",
-        configurationFactory=true,
-        metatype = true,
-        label = "Apache Jackrabbit Oak Segment-Tar NodeStore Factory",
-        description = "Factory allowing configuration of adjacent instances of " +
-                      "NodeStore implementation based on Segment model besides a default SegmentNodeStore in same setup."
+@Component(
+    configurationPolicy = ConfigurationPolicy.REQUIRE,
+    factory = "org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory"
 )
+@Designate(factory = true, ocd = SegmentNodeStoreFactory.Configuration.class)
 public class SegmentNodeStoreFactory {
 
-    @Property(
-            label = "Role",
-            description="As multiple SegmentNodeStores can be configured, this parameter defines the role " +
-                        "of 'this' SegmentNodeStore."
-    )
-    public static final String ROLE = "role";
+    private final Logger log = LoggerFactory.getLogger(getClass());
 
-    @Property(boolValue = false,
-            label = "Custom BlobStore",
-            description = "Boolean value indicating that a custom BlobStore is to be used. " +
-                    "By default large binary content would be stored within segment tar files"
-    )
-    public static final String CUSTOM_BLOB_STORE = "customBlobStore";
+    private static final long DEFAULT_BLOB_SNAPSHOT_INTERVAL = 12 * 60 * 60;
 
-    @Property(boolValue = false,
-            label = "Custom segment store",
-            description = "Boolean value indicating that a custom (non-tar) segment store is used"
+    private static final long DEFAULT_BLOB_GC_MAX_AGE = 24 * 60 * 60;
+
+    @ObjectClassDefinition(
+        name = "Apache Jackrabbit Oak Segment-Tar NodeStore Factory",
+        description = "Factory allowing configuration of adjacent instances of " +
+            "NodeStore implementation based on Segment model besides a default SegmentNodeStore in same setup."
     )
-    public static final String CUSTOM_SEGMENT_STORE = "customSegmentStore";
+    @interface Configuration {
 
-    @Property(boolValue = false,
-            label = "Register JCR descriptors as OSGi services",
-            description="Should only be done for one factory instance")
-    public static final String REGISTER_DESCRIPTORS = "registerDescriptors";
+        @AttributeDefinition(
+            name = "Repository Home Directory",
+            description = "Path on the file system where repository data will be stored. "
+                + "Defaults to the value of the framework property 'repository.home' or to 'repository' "
+                + "if that is neither specified."
+        )
+        String repository_home() default "repository";
+
+        @AttributeDefinition(
+            name = "Mode",
+            description = "TarMK mode (64 for memory mapped file access, 32 for normal file access). " +
+                "Default value is taken from the 'sun.arch.data.model' system property."
+        )
+        String tarmk_mode() default "";
+
+        @AttributeDefinition(
+            name = "Maximum tar file size (MB)",
+            description = "The maximum size of the tar files in megabytes. " +
+                "Default value is '" + DEFAULT_MAX_FILE_SIZE + "'."
+        )
+        int tarmk_size() default DEFAULT_MAX_FILE_SIZE;
+
+        @AttributeDefinition(
+            name = "Segment cache size (MB)",
+            description = "Cache size for storing most recently used segments in megabytes. " +
+                "Default value is '" + DEFAULT_SEGMENT_CACHE_MB + "'."
+        )
+        int segmentCache_size() default DEFAULT_SEGMENT_CACHE_MB;
+
+        @AttributeDefinition(
+            name = "String cache size (MB)",
+            description = "Cache size for storing most recently used strings in megabytes. " +
+                "Default value is '" + DEFAULT_STRING_CACHE_MB + "'."
+        )
+        int stringCache_size() default DEFAULT_STRING_CACHE_MB;
+
+        @AttributeDefinition(
+            name = "Template cache size (MB)",
+            description = "Cache size for storing most recently used templates in megabytes. " +
+                "Default value is '" + DEFAULT_TEMPLATE_CACHE_MB + "'."
+        )
+        int templateCache_size() default DEFAULT_TEMPLATE_CACHE_MB;
+
+        @AttributeDefinition(
+            name = "String deduplication cache size (#items)",
+            description = "Maximum number of strings to keep in the deduplication cache. " +
+                "Default value is '" + DEFAULT_STRING_CACHE_SIZE_OSGi + "'."
+        )
+        int stringDeduplicationCache_size() default DEFAULT_STRING_CACHE_SIZE_OSGi;
+
+        @AttributeDefinition(
+            name = "Template deduplication cache size (#items)",
+            description = "Maximum number of templates to keep in the deduplication cache. " +
+                "Default value is '" + DEFAULT_TEMPLATE_CACHE_SIZE_OSGi + "'."
+        )
+        int templateDeduplicationCache_size() default DEFAULT_TEMPLATE_CACHE_SIZE_OSGi;
+
+        @AttributeDefinition(
+            name = "Node deduplication cache size (#items)",
+            description = "Maximum number of node to keep in the deduplication cache. If the supplied " +
+                "value is not a power of 2, it will be rounded up to the next power of 2. " +
+                "Default value is '" + DEFAULT_NODE_CACHE_SIZE_OSGi + "'."
+        )
+        int nodeDeduplicationCache_size() default DEFAULT_NODE_CACHE_SIZE_OSGi;
+
+        @AttributeDefinition(
+            name = "Pause compaction",
+            description = "When set to true the compaction phase is skipped during garbage collection. " +
+                "Default value is '" + PAUSE_DEFAULT + "'."
+        )
+        boolean pauseCompaction() default PAUSE_DEFAULT;
+
+        @AttributeDefinition(
+            name = "Compaction retries",
+            description = "Number of tries to compact concurrent commits on top of already " +
+                "compacted commits. " +
+                "Default value is '" + RETRY_COUNT_DEFAULT + "'."
+        )
+        int compaction_retryCount() default RETRY_COUNT_DEFAULT;
+
+        @AttributeDefinition(
+            name = "Force compaction timeout",
+            description = "Number of seconds to attempt to force compact concurrent commits on top " +
+                "of already compacted commits after the maximum number of retries has been " +
+                "reached. Forced compaction tries to acquire an exclusive write lock on the " +
+                "node store, blocking concurrent write access as long as the lock is held. " +
+                "Default value is '" + FORCE_TIMEOUT_DEFAULT + "'."
+        )
+        int compaction_force_timeout() default FORCE_TIMEOUT_DEFAULT;
+
+        @AttributeDefinition(
+            name = "Garbage collection repository size threshold",
+            description = "Garbage collection will be skipped unless the repository grew at least by " +
+                "the number of bytes specified. " +
+                "Default value is '" + SIZE_DELTA_ESTIMATION_DEFAULT + "'."
+        )
+        long compaction_sizeDeltaEstimation() default SIZE_DELTA_ESTIMATION_DEFAULT;
+
+        @AttributeDefinition(
+            name = "Disable estimation phase",
+            description = "Disables the estimation phase allowing garbage collection to run unconditionally. " +
+                "Default value is '" + DISABLE_ESTIMATION_DEFAULT + "'."
+        )
+        boolean compaction_disableEstimation() default DISABLE_ESTIMATION_DEFAULT;
+
+        @AttributeDefinition(
+            name = "Compaction retained generations",
+            description = "Number of segment generations to retain during garbage collection. " +
+                "The number of generations defaults to " + RETAINED_GENERATIONS_DEFAULT + " and " +
+                "can't be changed. This configuration option is considered deprecated " +
+                "and will be removed in the future."
+        )
+        int compaction_retainedGenerations() default RETAINED_GENERATIONS_DEFAULT;
+
+        @AttributeDefinition(
+            name = "Compaction memory threshold",
+            description = "Threshold of available heap memory in percent of total heap memory below " +
+                "which the compaction phase is canceled. 0 disables heap memory monitoring. " +
+                "Default value is '" + MEMORY_THRESHOLD_DEFAULT + "'."
+        )
+        int compaction_memoryThreshold() default MEMORY_THRESHOLD_DEFAULT;
+
+        @AttributeDefinition(
+            name = "Compaction progress log",
+            description = "The number of nodes compacted after which a status message is logged. " +
+                "-1 disables progress logging. " +
+                "Default value is '" + GC_PROGRESS_LOG_DEFAULT + "'."
+        )
+        long compaction_progressLog() default GC_PROGRESS_LOG_DEFAULT;
+
+        @AttributeDefinition(
+            name = "Standby mode",
+            description = "Flag indicating this component will not register as a NodeStore but as a " +
+                "NodeStoreProvider instead. " +
+                "Default value is 'false'."
+        )
+        boolean standby() default false;
+
+        @AttributeDefinition(
+            name = "Custom blob store",
+            description = "Boolean value indicating that a custom BlobStore is used for storing " +
+                "large binary values."
+        )
+        boolean customBlobStore() default false;
 
-    private final Logger log = LoggerFactory.getLogger(getClass());
+        @AttributeDefinition(
+            name = "Custom segment store",
+            description = "Boolean value indicating that a custom (non-tar) segment store is used"
+        )
+        boolean customSegmentStore() default false;
+
+        @AttributeDefinition(
+            name = "Backup directory",
+            description = "Directory (relative to current working directory) for storing repository backups. " +
+                "Defaults to 'repository.home/segmentstore-backup'."
+        )
+        String repository_backup_dir() default "";
+
+        @AttributeDefinition(
+            name = "Blob gc max age (in secs)",
+            description = "The blob garbage collection logic will only consider those blobs which " +
+                "are not accessed recently (currentTime - lastModifiedTime > blobGcMaxAgeInSecs). " +
+                "For example with the default setting only those blobs which have been created " +
+                "at least 24 hours ago will be considered for garbage collection. " +
+                "Default value is '" + DEFAULT_BLOB_GC_MAX_AGE + "'."
+        )
+        long blobGcMaxAgeInSecs() default DEFAULT_BLOB_GC_MAX_AGE;
+
+        @AttributeDefinition(
+            name = "Blob tracking snapshot interval",
+            description = "Interval in seconds in which snapshots of locally tracked blob ids are " +
+                "taken and synchronized with the blob store. This should be configured to be " +
+                "less than the frequency of blob garbage collection so that deletions during blob " +
+                "garbage collection can be accounted for in the next garbage collection execution. " +
+                "Default value is '" + DEFAULT_BLOB_SNAPSHOT_INTERVAL + "'."
+        )
+        long blobTrackSnapshotIntervalInSecs() default DEFAULT_BLOB_SNAPSHOT_INTERVAL;
+
+        @AttributeDefinition(
+            name = "Role",
+            description = "As multiple SegmentNodeStores can be configured, this parameter defines the role " +
+                "of 'this' SegmentNodeStore."
+        )
+        String role() default "";
+
+        @AttributeDefinition(
+            name = "Register JCR descriptors as OSGi services",
+            description = "Should only be done for one factory instance"
+        )
+        boolean registerDescriptors() default false;
+    }
 
     @Reference(
-            cardinality = ReferenceCardinality.OPTIONAL_UNARY,
-            policy = ReferencePolicy.STATIC,
-            policyOption = ReferencePolicyOption.GREEDY,
-            target = ONLY_STANDALONE_TARGET
+        cardinality = ReferenceCardinality.OPTIONAL,
+        policy = ReferencePolicy.STATIC,
+        policyOption = ReferencePolicyOption.GREEDY,
+        target = ONLY_STANDALONE_TARGET
     )
     private volatile BlobStore blobStore;
 
     @Reference(
-            cardinality = ReferenceCardinality.OPTIONAL_UNARY,
-            policy = ReferencePolicy.STATIC,
-            policyOption = ReferencePolicyOption.GREEDY
+        cardinality = ReferenceCardinality.OPTIONAL,
+        policy = ReferencePolicy.STATIC,
+        policyOption = ReferencePolicyOption.GREEDY
     )
     private volatile SegmentNodeStorePersistence segmentStore;
 
     @Reference
     private StatisticsProvider statisticsProvider = StatisticsProvider.NOOP;
 
-    private Closer registrations = Closer.create();
+    private final Closer registrations = Closer.create();
 
     @Activate
-    public void activate(ComponentContext context) throws IOException {
-        String role = property(ROLE, context);
-        // In secondaryNodeStore mode customBlobStore is always enabled
-        boolean isSecondaryStoreMode = "secondary".equals(role);
-        boolean customBlobStore = Boolean.parseBoolean(property(CUSTOM_BLOB_STORE, context)) || isSecondaryStoreMode;
-        boolean customSegmentStore = Boolean.parseBoolean(property(CUSTOM_SEGMENT_STORE, context));
-        boolean registerRepositoryDescriptors = Boolean.parseBoolean(property(REGISTER_DESCRIPTORS, context));
-        log.info("activate: SegmentNodeStore '" + role + "' starting.");
+    public void activate(ComponentContext context, Configuration configuration) throws IOException {
+        log.info("activate: SegmentNodeStore '" + configuration.role() + "' starting.");
 
-        if (blobStore == null && customBlobStore) {
-            log.info("BlobStore use enabled. SegmentNodeStore would be initialized when BlobStore would be available");
+        if (configuration.role().isEmpty()) {
             return;
         }
 
-        if (segmentStore == null && customSegmentStore) {
-            log.info("customSegmentStore enabled. SegmentNodeStore will be initialized once the custom segment store becomes available");
+        OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext());
+        SegmentNodeStore store = registerSegmentStore(
+            context,
+            configuration,
+            blobStore,
+            segmentStore,
+            getRoleStatisticsProvider(statisticsProvider, configuration.role()),
+            registrations,
+            whiteboard,
+            configuration.role(),
+            log
+        );
+
+        if (store == null) {
             return;
         }
 
-        if (role != null) {
-            registrations = Closer.create();
-            OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext());
-            final SegmentNodeStore store = SegmentNodeStoreService.registerSegmentStore(context, blobStore, segmentStore,
-                    getRoleStatisticsProvider(statisticsProvider, role), registrations, whiteboard, role, registerRepositoryDescriptors);
-            if (store != null) {
-                Map<String, Object> props = new HashMap<String, Object>();
-                props.put(NodeStoreProvider.ROLE, role);
-
-                registrations
-                        .register(asCloseable(whiteboard.register(NodeStoreProvider.class, new NodeStoreProvider() {
-
-                            @Override
-                            public NodeStore getNodeStore() {
-                                return store;
-                            }
-                        }, props)));
-                log.info("Registered NodeStoreProvider backed by SegmentNodeStore of type '{}'", role);
-            }
-        }
+        Map<String, Object> props = new HashMap<String, Object>();
+        props.put(NodeStoreProvider.ROLE, configuration.role());
+        registrations.register(asCloseable(whiteboard.register(NodeStoreProvider.class, () -> store, props)));
+        log.info("Registered NodeStoreProvider backed by SegmentNodeStore of type '{}'", configuration.role());
     }
 
     @Deactivate
     public void deactivate() {
-        if (registrations != null) {
-            IOUtils.closeQuietly(registrations);
-            registrations = null;
-        }
+        closeQuietly(registrations);
     }
 
     private static Closeable asCloseable(final Registration r) {
@@ -180,20 +361,246 @@ public class SegmentNodeStoreFactory {
         return lookupConfigurationThenFramework(context, name);
     }
 
-    private static StatisticsProvider getRoleStatisticsProvider(StatisticsProvider delegate, String role) {
-       RepositoryStatistics repositoryStatistics = new RepositoryStatistics() {
-                @Override
-                public TimeSeries getTimeSeries(Type type) {
-                    return getTimeSeries(type.name(), type.isResetValueEachSecond());
+    private static SegmentNodeStore registerSegmentStore(
+        ComponentContext context,
+        Configuration configuration,
+        BlobStore blobStore,
+        SegmentNodeStorePersistence segmentStore,
+        StatisticsProvider statisticsProvider,
+        Closer closer,
+        Whiteboard whiteboard,
+        String role,
+        Logger logger
+    ) throws IOException {
+        return SegmentNodeStoreRegistrar.registerSegmentNodeStore(new SegmentNodeStoreRegistrar.Configuration() {
+
+            String appendRole(String name) {
+                return name + "-" + role;
+            }
+
+            int roundToNextPowerOfTwo(int size) {
+                return 1 << (32 - Integer.numberOfLeadingZeros(Math.max(0, size - 1)));
+            }
+
+            String getMode() {
+                String mode = configuration.tarmk_mode();
+                if (isNullOrEmpty(mode)) {
+                    return System.getProperty("tarmk.mode", System.getProperty("sun.arch.data.model", "32"));
                 }
+                return mode;
+            }
 
-                @Override
-                public TimeSeries getTimeSeries(String type, boolean resetValueEachSecond) {
-                    return delegate.getStats().getTimeSeries(addRoleToName(type, role), resetValueEachSecond);
+            int getCacheSize(String name, int otherwise) {
+                Integer size = Integer.getInteger(name);
+                if (size != null) {
+                    return size;
                 }
+                return otherwise;
+            }
+
+            @Override
+            public boolean isPrimarySegmentStore() {
+                return false;
+            }
+
+            @Override
+            public boolean isSecondarySegmentStore() {
+                return "secondary".equals(role);
+            }
+
+            @Override
+            public boolean isStandbyInstance() {
+                return configuration.standby();
+            }
+
+            @Override
+            public String getRole() {
+                return role;
+            }
+
+            @Override
+            public int getRetainedGenerations() {
+                return configuration.compaction_retainedGenerations();
+            }
+
+            @Override
+            public int getDefaultRetainedGenerations() {
+                return RETAINED_GENERATIONS_DEFAULT;
+            }
+
+            @Override
+            public boolean getPauseCompaction() {
+                return configuration.pauseCompaction();
+            }
+
+            @Override
+            public int getRetryCount() {
+                return configuration.compaction_retryCount();
+            }
+
+            @Override
+            public int getForceCompactionTimeout() {
+                return configuration.compaction_force_timeout();
+            }
+
+            @Override
+            public long getSizeDeltaEstimation() {
+                return configuration.compaction_sizeDeltaEstimation();
+            }
+
+            @Override
+            public int getMemoryThreshold() {
+                return configuration.compaction_memoryThreshold();
+            }
+
+            @Override
+            public boolean getDisableEstimation() {
+                return configuration.compaction_disableEstimation();
+            }
+
+            @Override
+            public long getGCProcessLog() {
+                return configuration.compaction_progressLog();
+            }
+
+            @Override
+            public File getSegmentDirectory() {
+                return new File(getRepositoryHome(), appendRole("segmentstore"));
+            }
+
+            @Override
+            public int getSegmentCacheSize() {
+                return getCacheSize("segmentCache.size", configuration.segmentCache_size());
+            }
+
+            @Override
+            public int getStringCacheSize() {
+                return getCacheSize("stringCache.size", configuration.stringCache_size());
+            }
+
+            @Override
+            public int getTemplateCacheSize() {
+                return getCacheSize("templateCache.size", configuration.templateCache_size());
+            }
+
+            @Override
+            public int getStringDeduplicationCacheSize() {
+                return getCacheSize("stringDeduplicationCache.size", configuration.stringDeduplicationCache_size());
+            }
+
+            @Override
+            public int getTemplateDeduplicationCacheSize() {
+                return getCacheSize("templateDeduplicationCache.size", configuration.templateDeduplicationCache_size());
+            }
+
+            @Override
+            public int getNodeDeduplicationCacheSize() {
+                return roundToNextPowerOfTwo(getCacheSize("nodeDeduplicationCache.size", configuration.nodeDeduplicationCache_size()));
+            }
+
+            @Override
+            public int getMaxFileSize() {
+                return configuration.tarmk_size();
+            }
+
+            @Override
+            public boolean getMemoryMapping() {
+                return getMode().equals("64");
+            }
+
+            @Override
+            public boolean hasCustomBlobStore() {
+                return configuration.customBlobStore();
+            }
+
+            @Override
+            public boolean hasCustomSegmentStore() {
+                return configuration.customSegmentStore();
+            }
+
+            @Override
+            public boolean registerDescriptors() {
+                return configuration.registerDescriptors();
+            }
+
+            @Override
+            public String getRepositoryHome() {
+                return configuration.repository_home();
+            }
+
+            @Override
+            public long getBlobSnapshotInterval() {
+                return configuration.blobTrackSnapshotIntervalInSecs();
+            }
+
+            @Override
+            public long getBlobGcMaxAge() {
+                return configuration.blobGcMaxAgeInSecs();
+            }
+
+            @Override
+            public File getBackupDirectory() {
+                String backupDirectory = configuration.repository_backup_dir();
+                if (isNullOrEmpty(backupDirectory)) {
+                    return new File(getRepositoryHome(), appendRole("segmentstore-backup"));
+                }
+                return new File(backupDirectory);
+            }
+
+            @Override
+            public Whiteboard getWhiteboard() {
+                return whiteboard;
+            }
+
+            @Override
+            public Closer getCloser() {
+                return closer;
+            }
+
+            @Override
+            public Logger getLogger() {
+                return logger;
+            }
+
+            @Override
+            public StatisticsProvider getStatisticsProvider() {
+                return statisticsProvider;
+            }
+
+            @Override
+            public BlobStore getBlobStore() {
+                return blobStore;
+            }
+
+            @Override
+            public SegmentNodeStorePersistence getSegmentNodeStorePersistence() {
+                return segmentStore;
+            }
+
+            @Override
+            public BundleContext getBundleContext() {
+                return context.getBundleContext();
+            }
+
+        });
+    }
+
+    private static StatisticsProvider getRoleStatisticsProvider(StatisticsProvider delegate, String role) {
+        RepositoryStatistics repositoryStatistics = new RepositoryStatistics() {
+
+            @Override
+            public TimeSeries getTimeSeries(Type type) {
+                return getTimeSeries(type.name(), type.isResetValueEachSecond());
+            }
+
+            @Override
+            public TimeSeries getTimeSeries(String type, boolean resetValueEachSecond) {
+                return delegate.getStats().getTimeSeries(addRoleToName(type, role), resetValueEachSecond);
+            }
         };
 
         return new StatisticsProvider() {
+
             @Override
             public RepositoryStatistics getStats() {
                 return repositoryStatistics;
@@ -218,12 +625,12 @@ public class SegmentNodeStoreFactory {
             public HistogramStats getHistogram(String name, StatsOptions options) {
                 return delegate.getHistogram(addRoleToName(name, role), options);
             }
+
         };
     }
 
     private static String addRoleToName(String name, String role) {
-        return new StringBuilder(role).append('.').append(name).toString();
+        return role + '.' + name;
     }
 
-
 }

Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitorService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitorService.java?rev=1835837&r1=1835836&r2=1835837&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitorService.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreMonitorService.java Fri Jul 13 13:23:24 2018
@@ -19,45 +19,44 @@
 
 package org.apache.jackrabbit.oak.segment;
 
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.ConfigurationPolicy;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.PropertyUnbounded;
-import org.apache.felix.scr.annotations.Reference;
 import org.apache.jackrabbit.oak.commons.PropertiesUtil;
-import org.osgi.service.component.ComponentContext;
+import org.apache.jackrabbit.oak.segment.SegmentNodeStoreMonitorService.Configuration;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 
 /**
  * An OSGi wrapper for segment node store monitoring configurations.
  */
-@Component(policy = ConfigurationPolicy.REQUIRE,
-        metatype = true,
-        label = "Oak Segment Tar Monitoring service",
-        description = "This service is responsible for different configurations related to " + 
-                "Oak Segment Tar read/write monitoring."
-)
+@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
+@Designate(ocd = Configuration.class)
 public class SegmentNodeStoreMonitorService {
-    
-    @Property(label = "Writer groups",
-            unbounded = PropertyUnbounded.ARRAY,
-            description = "Writer groups for which commits are tracked individually"
+
+    @ObjectClassDefinition(
+        name = "Oak Segment Tar Monitoring service",
+        description = "This service is responsible for different configurations related to " +
+            "Oak Segment Tar read/write monitoring."
     )
-    private static final String COMMITS_TRACKER_WRITER_GROUPS = "commitsTrackerWriterGroups";
+    @interface Configuration {
+
+        @AttributeDefinition(
+            name = "Writer groups",
+            description = "Writer groups for which commits are tracked individually"
+        )
+        String[] commitsTrackerWriterGroups() default {};
+
+    }
 
     @Reference
     private SegmentNodeStoreStatsMBean snsStatsMBean;
-    
+
     @Activate
-    public void activate(ComponentContext context, Map<String, ?> config) throws IOException {
-        augmentSegmentNodeStoreStatsMBean(config);
+    public void activate(Configuration config) {
+        snsStatsMBean.setWriterGroupsForLastMinuteCounts(PropertiesUtil.toStringArray(config.commitsTrackerWriterGroups(), null));
     }
 
-    private void augmentSegmentNodeStoreStatsMBean(Map<String, ?> config) {
-        snsStatsMBean.setWriterGroupsForLastMinuteCounts(
-                PropertiesUtil.toStringArray(config.get(COMMITS_TRACKER_WRITER_GROUPS), null));
-    }
 }

Added: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java?rev=1835837&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java (added)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java Fri Jul 13 13:23:24 2018
@@ -0,0 +1,525 @@
+/*
+ * 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.segment;
+
+import static org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils.isShared;
+import static org.apache.jackrabbit.oak.segment.SegmentNotFoundExceptionListener.IGNORE_SNFE;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETAINED_GENERATIONS_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder;
+import static org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo.getOrCreateId;
+
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.io.Closer;
+import org.apache.jackrabbit.commons.SimpleValueFactory;
+import org.apache.jackrabbit.oak.api.Descriptors;
+import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
+import org.apache.jackrabbit.oak.api.jmx.CheckpointMBean;
+import org.apache.jackrabbit.oak.api.jmx.FileStoreBackupRestoreMBean;
+import org.apache.jackrabbit.oak.backup.impl.FileStoreBackupRestoreImpl;
+import org.apache.jackrabbit.oak.cache.CacheStats;
+import org.apache.jackrabbit.oak.plugins.blob.BlobGC;
+import org.apache.jackrabbit.oak.plugins.blob.BlobGCMBean;
+import org.apache.jackrabbit.oak.plugins.blob.BlobGarbageCollector;
+import org.apache.jackrabbit.oak.plugins.blob.BlobTrackingStore;
+import org.apache.jackrabbit.oak.plugins.blob.MarkSweepGarbageCollector;
+import org.apache.jackrabbit.oak.plugins.blob.SharedDataStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.BlobIdTracker;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils;
+import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions;
+import org.apache.jackrabbit.oak.segment.compaction.SegmentRevisionGC;
+import org.apache.jackrabbit.oak.segment.compaction.SegmentRevisionGCMBean;
+import org.apache.jackrabbit.oak.segment.file.FileStore;
+import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder;
+import org.apache.jackrabbit.oak.segment.file.FileStoreGCMonitor;
+import org.apache.jackrabbit.oak.segment.file.FileStoreStatsMBean;
+import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.oak.segment.file.MetricsIOMonitor;
+import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore;
+import org.apache.jackrabbit.oak.spi.cluster.ClusterRepositoryInfo;
+import org.apache.jackrabbit.oak.spi.commit.ObserverTracker;
+import org.apache.jackrabbit.oak.spi.descriptors.GenericDescriptors;
+import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
+import org.apache.jackrabbit.oak.spi.gc.GCMonitorTracker;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.RevisionGC;
+import org.apache.jackrabbit.oak.spi.state.RevisionGCMBean;
+import org.apache.jackrabbit.oak.spi.whiteboard.AbstractServiceTracker;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardExecutor;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
+import org.apache.jackrabbit.oak.stats.Clock;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.slf4j.Logger;
+
+class SegmentNodeStoreRegistrar {
+
+    static SegmentNodeStore registerSegmentNodeStore(Configuration cfg) throws IOException {
+        return new SegmentNodeStoreRegistrar(cfg).register();
+    }
+
+    interface Configuration {
+
+        boolean isPrimarySegmentStore();
+
+        boolean isSecondarySegmentStore();
+
+        boolean isStandbyInstance();
+
+        String getRole();
+
+        int getRetainedGenerations();
+
+        int getDefaultRetainedGenerations();
+
+        boolean getPauseCompaction();
+
+        int getRetryCount();
+
+        int getForceCompactionTimeout();
+
+        long getSizeDeltaEstimation();
+
+        int getMemoryThreshold();
+
+        boolean getDisableEstimation();
+
+        long getGCProcessLog();
+
+        File getSegmentDirectory();
+
+        int getSegmentCacheSize();
+
+        int getStringCacheSize();
+
+        int getTemplateCacheSize();
+
+        int getStringDeduplicationCacheSize();
+
+        int getTemplateDeduplicationCacheSize();
+
+        int getNodeDeduplicationCacheSize();
+
+        int getMaxFileSize();
+
+        boolean getMemoryMapping();
+
+        boolean hasCustomBlobStore();
+
+        boolean hasCustomSegmentStore();
+
+        boolean registerDescriptors();
+
+        String getRepositoryHome();
+
+        long getBlobSnapshotInterval();
+
+        long getBlobGcMaxAge();
+
+        File getBackupDirectory();
+
+        Whiteboard getWhiteboard();
+
+        Closer getCloser();
+
+        Logger getLogger();
+
+        StatisticsProvider getStatisticsProvider();
+
+        BlobStore getBlobStore();
+
+        SegmentNodeStorePersistence getSegmentNodeStorePersistence();
+
+        BundleContext getBundleContext();
+
+    }
+
+    private final Configuration cfg;
+
+    private SegmentNodeStoreRegistrar(Configuration cfg) {
+        this.cfg = cfg;
+    }
+
+    private SegmentNodeStore register() throws IOException {
+        if (cfg.getBlobStore() == null && (cfg.hasCustomBlobStore() || cfg.isSecondarySegmentStore())) {
+            cfg.getLogger().info("BlobStore enabled. SegmentNodeStore will be initialized once the blob store becomes available");
+            return null;
+        }
+
+        if (cfg.getSegmentNodeStorePersistence() == null && cfg.hasCustomSegmentStore()) {
+            cfg.getLogger().info("customSegmentStore enabled. SegmentNodeStore will be initialized once the custom segment store becomes available");
+            return null;
+        }
+
+        // Listen for GCMonitor services
+        GCMonitor gcMonitor = GCMonitor.EMPTY;
+
+        if (cfg.isPrimarySegmentStore()) {
+            GCMonitorTracker tracker = new GCMonitorTracker();
+            tracker.start(cfg.getWhiteboard());
+            registerCloseable(tracker);
+            gcMonitor = tracker;
+        }
+
+        // Create the gc options
+        if (cfg.getRetainedGenerations() != cfg.getDefaultRetainedGenerations()) {
+            cfg.getLogger().warn(
+                "The number of retained generations defaults to {} and can't be " +
+                    "changed. This configuration option is considered deprecated " +
+                    "and will be removed in the future.",
+                RETAINED_GENERATIONS_DEFAULT
+            );
+        }
+        SegmentGCOptions gcOptions = new SegmentGCOptions(cfg.getPauseCompaction(), cfg.getRetryCount(), cfg.getForceCompactionTimeout())
+            .setGcSizeDeltaEstimation(cfg.getSizeDeltaEstimation())
+            .setMemoryThreshold(cfg.getMemoryThreshold())
+            .setEstimationDisabled(cfg.getDisableEstimation())
+            .setGCLogInterval(cfg.getGCProcessLog());
+        if (cfg.isStandbyInstance()) {
+            gcOptions.setRetainedGenerations(1);
+        }
+
+        // Build the FileStore
+        FileStoreBuilder builder = fileStoreBuilder(cfg.getSegmentDirectory())
+            .withSegmentCacheSize(cfg.getSegmentCacheSize())
+            .withStringCacheSize(cfg.getStringCacheSize())
+            .withTemplateCacheSize(cfg.getTemplateCacheSize())
+            .withStringDeduplicationCacheSize(cfg.getStringDeduplicationCacheSize())
+            .withTemplateDeduplicationCacheSize(cfg.getTemplateDeduplicationCacheSize())
+            .withNodeDeduplicationCacheSize(cfg.getNodeDeduplicationCacheSize())
+            .withMaxFileSize(cfg.getMaxFileSize())
+            .withMemoryMapping(cfg.getMemoryMapping())
+            .withGCMonitor(gcMonitor)
+            .withIOMonitor(new MetricsIOMonitor(cfg.getStatisticsProvider()))
+            .withStatisticsProvider(cfg.getStatisticsProvider())
+            .withGCOptions(gcOptions);
+
+        if (cfg.hasCustomBlobStore() && cfg.getBlobStore() != null) {
+            cfg.getLogger().info("Initializing SegmentNodeStore with BlobStore [{}]", cfg.getBlobStore());
+            builder.withBlobStore(cfg.getBlobStore());
+        }
+
+        if (cfg.hasCustomSegmentStore() && cfg.getSegmentNodeStorePersistence() != null) {
+            cfg.getLogger().info("Initializing SegmentNodeStore with custom persistence [{}]", cfg.getSegmentNodeStorePersistence());
+            builder.withCustomPersistence(cfg.getSegmentNodeStorePersistence());
+        }
+
+        if (cfg.isStandbyInstance()) {
+            builder.withSnfeListener(IGNORE_SNFE);
+        }
+
+        FileStore store;
+        try {
+            store = builder.build();
+        } catch (InvalidFileStoreVersionException e) {
+            cfg.getLogger().error("The storage format is not compatible with this version of Oak Segment Tar", e);
+            return null;
+        }
+        registerCloseable(store);
+
+        // Listen for Executor services on the whiteboard
+
+        WhiteboardExecutor executor = new WhiteboardExecutor();
+        executor.start(cfg.getWhiteboard());
+        registerCloseable(executor);
+
+        // Expose stats about the segment cache
+
+        CacheStatsMBean segmentCacheStats = store.getSegmentCacheStats();
+        registerCloseable(registerMBean(
+            CacheStatsMBean.class,
+            segmentCacheStats,
+            CacheStats.TYPE,
+            segmentCacheStats.getName()
+        ));
+
+        // Expose stats about the string and template caches
+
+        CacheStatsMBean stringCacheStats = store.getStringCacheStats();
+        registerCloseable(registerMBean(
+            CacheStatsMBean.class,
+            stringCacheStats,
+            CacheStats.TYPE,
+            stringCacheStats.getName()
+        ));
+
+        CacheStatsMBean templateCacheStats = store.getTemplateCacheStats();
+        registerCloseable(registerMBean(
+            CacheStatsMBean.class,
+            templateCacheStats,
+            CacheStats.TYPE,
+            templateCacheStats.getName()
+        ));
+
+        WriterCacheManager cacheManager = builder.getCacheManager();
+        CacheStatsMBean stringDeduplicationCacheStats = cacheManager.getStringCacheStats();
+        if (stringDeduplicationCacheStats != null) {
+            registerCloseable(registerMBean(
+                CacheStatsMBean.class,
+                stringDeduplicationCacheStats,
+                CacheStats.TYPE,
+                stringDeduplicationCacheStats.getName()
+            ));
+        }
+
+        CacheStatsMBean templateDeduplicationCacheStats = cacheManager.getTemplateCacheStats();
+        if (templateDeduplicationCacheStats != null) {
+            registerCloseable(registerMBean(
+                CacheStatsMBean.class,
+                templateDeduplicationCacheStats,
+                CacheStats.TYPE,
+                templateDeduplicationCacheStats.getName()
+            ));
+        }
+
+        CacheStatsMBean nodeDeduplicationCacheStats = cacheManager.getNodeCacheStats();
+        if (nodeDeduplicationCacheStats != null) {
+            registerCloseable(registerMBean(
+                CacheStatsMBean.class,
+                nodeDeduplicationCacheStats,
+                CacheStats.TYPE,
+                nodeDeduplicationCacheStats.getName()
+            ));
+        }
+
+        // Expose an MBean to managing and monitoring garbage collection
+
+        FileStoreGCMonitor monitor = new FileStoreGCMonitor(Clock.SIMPLE);
+        registerCloseable(register(
+            GCMonitor.class,
+            monitor
+        ));
+        if (!cfg.isStandbyInstance()) {
+            registerCloseable(registerMBean(
+                SegmentRevisionGC.class,
+                new SegmentRevisionGCMBean(store, gcOptions, monitor),
+                SegmentRevisionGC.TYPE,
+                "Segment node store revision garbage collection"
+            ));
+        }
+
+        registerCloseable(registerMBean(
+            RevisionGCMBean.class,
+            new RevisionGC(store.getGCRunner(), store::cancelGC, monitor::getStatus, executor),
+            RevisionGCMBean.TYPE,
+            "Revision garbage collection"
+        ));
+
+        // Expose statistics about the FileStore
+
+        registerCloseable(registerMBean(
+            FileStoreStatsMBean.class,
+            store.getStats(),
+            FileStoreStatsMBean.TYPE,
+            "FileStore statistics"
+        ));
+
+        // register segment node store
+
+        SegmentNodeStore.SegmentNodeStoreBuilder segmentNodeStoreBuilder = SegmentNodeStoreBuilders.builder(store).withStatisticsProvider(cfg.getStatisticsProvider());
+        if (cfg.isStandbyInstance() || !cfg.isPrimarySegmentStore()) {
+            segmentNodeStoreBuilder.dispatchChanges(false);
+        }
+        SegmentNodeStore segmentNodeStore = segmentNodeStoreBuilder.build();
+
+        if (cfg.isPrimarySegmentStore()) {
+            ObserverTracker observerTracker = new ObserverTracker(segmentNodeStore);
+            observerTracker.start(cfg.getBundleContext());
+            registerCloseable(observerTracker);
+        }
+
+        if (cfg.isPrimarySegmentStore()) {
+            registerCloseable(registerMBean(
+                CheckpointMBean.class,
+                new SegmentCheckpointMBean(segmentNodeStore),
+                CheckpointMBean.TYPE,
+                "Segment node store checkpoint management"
+            ));
+        }
+
+        if (cfg.registerDescriptors()) {
+            // ensure a clusterId is initialized
+            // and expose it as 'oak.clusterid' repository descriptor
+            GenericDescriptors clusterIdDesc = new GenericDescriptors();
+            clusterIdDesc.put(
+                ClusterRepositoryInfo.OAK_CLUSTERID_REPOSITORY_DESCRIPTOR_KEY,
+                new SimpleValueFactory().createValue(getOrCreateId(segmentNodeStore)),
+                true,
+                false
+            );
+            registerCloseable(register(Descriptors.class, clusterIdDesc));
+            // Register "discovery lite" descriptors
+            registerCloseable(register(Descriptors.class, new SegmentDiscoveryLiteDescriptors(segmentNodeStore)));
+        }
+
+        // If a shared data store register the repo id in the data store
+        if (cfg.isPrimarySegmentStore() && isShared(cfg.getBlobStore())) {
+            SharedDataStore sharedDataStore = (SharedDataStore) cfg.getBlobStore();
+            try {
+                sharedDataStore.addMetadataRecord(new ByteArrayInputStream(new byte[0]), SharedDataStoreUtils.SharedStoreRecordType.REPOSITORY.getNameFromId(getOrCreateId(segmentNodeStore)));
+            } catch (Exception e) {
+                throw new IOException("Could not register a unique repositoryId", e);
+            }
+            if (cfg.getBlobStore() instanceof BlobTrackingStore) {
+                BlobTrackingStore trackingStore = (BlobTrackingStore) cfg.getBlobStore();
+                if (trackingStore.getTracker() != null) {
+                    trackingStore.getTracker().close();
+                }
+                trackingStore.addTracker(new BlobIdTracker(cfg.getRepositoryHome(), getOrCreateId(segmentNodeStore), cfg.getBlobSnapshotInterval(), sharedDataStore));
+            }
+        }
+
+        if (cfg.isPrimarySegmentStore() && cfg.getBlobStore() instanceof GarbageCollectableBlobStore) {
+            BlobGarbageCollector gc = new MarkSweepGarbageCollector(
+                new SegmentBlobReferenceRetriever(store),
+                (GarbageCollectableBlobStore) cfg.getBlobStore(),
+                executor,
+                TimeUnit.SECONDS.toMillis(cfg.getBlobGcMaxAge()),
+                getOrCreateId(segmentNodeStore),
+                cfg.getWhiteboard(),
+                cfg.getStatisticsProvider()
+            );
+            registerCloseable(registerMBean(
+                BlobGCMBean.class,
+                new BlobGC(gc, executor),
+                BlobGCMBean.TYPE,
+                "Segment node store blob garbage collection"
+            ));
+        }
+
+        // Expose an MBean for backup/restore operations
+
+        registerCloseable(registerMBean(
+            FileStoreBackupRestoreMBean.class,
+            new FileStoreBackupRestoreImpl(
+                segmentNodeStore,
+                store.getRevisions(),
+                store.getReader(),
+                cfg.getBackupDirectory(),
+                executor
+            ),
+            FileStoreBackupRestoreMBean.TYPE,
+            "Segment node store backup/restore"
+        ));
+
+        // Expose statistics about the SegmentNodeStore
+
+        registerCloseable(registerMBean(
+            SegmentNodeStoreStatsMBean.class,
+            segmentNodeStore.getStats(),
+            SegmentNodeStoreStatsMBean.TYPE,
+            "SegmentNodeStore statistics"
+        ));
+
+        if (cfg.isPrimarySegmentStore()) {
+            cfg.getLogger().info("Primary SegmentNodeStore initialized");
+        } else {
+            cfg.getLogger().info("Secondary SegmentNodeStore initialized, role={}", cfg.getRole());
+        }
+
+        // Register a factory service to expose the FileStore
+        registerCloseable(register(
+            SegmentStoreProvider.class,
+            new DefaultSegmentStoreProvider(store)
+        ));
+
+        if (cfg.isStandbyInstance()) {
+            return segmentNodeStore;
+        }
+
+        if (cfg.isPrimarySegmentStore()) {
+            Map<String, Object> props = new HashMap<>();
+            props.put(Constants.SERVICE_PID, SegmentNodeStore.class.getName());
+            props.put("oak.nodestore.description", new String[] {"nodeStoreType=segment"});
+            registerCloseable(register(NodeStore.class, segmentNodeStore, props));
+        }
+
+        return segmentNodeStore;
+    }
+
+    private <T> Registration registerMBean(Class<T> clazz, T bean, String type, String name) {
+        return registerMBean(clazz, bean, type, name, new HashMap<>());
+    }
+
+    private <T> Registration registerMBean(Class<T> clazz, T bean, String type, String name, Map<String, String> attributes) {
+        return WhiteboardUtils.registerMBean(cfg.getWhiteboard(), clazz, bean, type, maybeAppendRole(name), maybePutRoleAttribute(attributes));
+    }
+
+    private <T> Registration register(Class<T> clazz, T service) {
+        return register(clazz, service, new HashMap<>());
+    }
+
+    private <T> Registration register(Class<T> clazz, T service, Map<String, Object> properties) {
+        return cfg.getWhiteboard().register(clazz, service, maybePutRoleProperty(properties));
+    }
+
+    private String maybeAppendRole(String name) {
+        if (cfg.getRole() != null) {
+            return name + " - " + cfg.getRole();
+        }
+        return name;
+    }
+
+    private String jmxRole() {
+        return cfg.getRole().replaceAll(":", "-");
+    }
+
+    private Map<String, String> maybePutRoleAttribute(Map<String, String> attributes) {
+        if (cfg.getRole() != null) {
+            attributes.put("role", jmxRole());
+        }
+        return attributes;
+    }
+
+    private Map<String, Object> maybePutRoleProperty(Map<String, Object> attributes) {
+        if (cfg.getRole() != null) {
+            attributes.put("role", cfg.getRole());
+        }
+        return attributes;
+    }
+
+    private void registerCloseable(Closeable c) {
+        cfg.getCloser().register(c);
+    }
+
+    private void registerCloseable(final AbstractServiceTracker<?> t) {
+        registerCloseable((Closeable) t::stop);
+    }
+
+    private void registerCloseable(final Registration r) {
+        registerCloseable((Closeable) r::unregister);
+    }
+
+    private void registerCloseable(final ObserverTracker t) {
+        registerCloseable((Closeable) t::stop);
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreRegistrar.java
------------------------------------------------------------------------------
    svn:eol-style = native