Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java?rev=1836149&r1=1836148&r2=1836149&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java (original) +++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java Wed Jul 18 07:42:54 2018 @@ -21,9 +21,40 @@ package org.apache.jackrabbit.oak.segmen import static com.google.common.base.Strings.isNullOrEmpty; import static org.apache.jackrabbit.oak.commons.IOUtils.closeQuietly; +import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toBoolean; +import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toInteger; +import static org.apache.jackrabbit.oak.commons.PropertiesUtil.toLong; +import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework; +import static org.apache.jackrabbit.oak.plugins.blob.datastore.SharedDataStoreUtils.isShared; 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.SegmentNodeStoreService.BACKUP_DIRECTORY; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.COMPACTION_DISABLE_ESTIMATION; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.COMPACTION_FORCE_TIMEOUT; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.COMPACTION_RETRY_COUNT; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.COMPACTION_SIZE_DELTA_ESTIMATION; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.CUSTOM_BLOB_STORE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.CUSTOM_SEGMENT_STORE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.DEFAULT_BLOB_GC_MAX_AGE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.DEFAULT_BLOB_SNAPSHOT_INTERVAL; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.GC_PROGRESS_LOG; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.MEMORY_THRESHOLD; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.MODE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.NODE_DEDUPLICATION_CACHE_SIZE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.PAUSE_COMPACTION; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.PROP_BLOB_GC_MAX_AGE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.PROP_BLOB_SNAPSHOT_INTERVAL; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.REPOSITORY_HOME_DIRECTORY; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.RETAINED_GENERATIONS; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.SEGMENT_CACHE_SIZE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.SIZE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.STANDBY; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.STRING_CACHE_SIZE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.STRING_DEDUPLICATION_CACHE_SIZE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.TEMPLATE_CACHE_SIZE; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.TEMPLATE_DEDUPLICATION_CACHE_SIZE; +import static org.apache.jackrabbit.oak.segment.SegmentNotFoundExceptionListener.IGNORE_SNFE; 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; @@ -36,511 +67,996 @@ import static org.apache.jackrabbit.oak. 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.segment.file.FileStoreBuilder.fileStoreBuilder; import static org.apache.jackrabbit.oak.spi.blob.osgi.SplitBlobStoreService.ONLY_STANDALONE_TARGET; +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 javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.google.common.base.Supplier; 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.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.cache.CacheStatsMBeanWrapper; import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard; +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.SharedStoreRecordType; +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.file.tar.SegmentTarReader; import org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence; +import org.apache.jackrabbit.oak.segment.util.RoleUtils; 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.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; /** * An OSGi wrapper for the segment node store. */ -@Component(configurationPolicy = ConfigurationPolicy.REQUIRE) -@Designate(ocd = SegmentNodeStoreService.Configuration.class) +@Component(policy = ConfigurationPolicy.REQUIRE, + metatype = true, + label = "Oak Segment Tar NodeStore service", + description = "Apache Jackrabbit Oak NodeStore implementation based on the segment model. " + + "For configuration refer to http://jackrabbit.apache.org/oak/docs/osgi_config.html#SegmentNodeStore. " + + "Note that for system stability purpose it is advisable to not change these settings " + + "at runtime. Instead the config change should be done via file system based config " + + "file and this view should ONLY be used to determine which options are supported." +) public class SegmentNodeStoreService { private static final Logger log = LoggerFactory.getLogger(SegmentNodeStoreService.class); - // TODO(frm) This is only exposed to tests. Should it be removed? - public static final String CUSTOM_BLOB_STORE = "customBlobStore"; - - // TODO(frm) This is only exposed to tests. Should it be removed? - public static final String REPOSITORY_HOME_DIRECTORY = "repository.home"; - - private static final long DEFAULT_BLOB_SNAPSHOT_INTERVAL = 12 * 60 * 60; - - private static final long DEFAULT_BLOB_GC_MAX_AGE = 24 * 60 * 60; - - @ObjectClassDefinition( - name = "Oak Segment Tar NodeStore service", - description = "Apache Jackrabbit Oak NodeStore implementation based on " + - "the segment model. For configuration refer to http://jackrabbit.apache.org/oak/docs/osgi_config.html#SegmentNodeStore. " + - "Note that for system stability purpose it is advisable to not " + - "change these settings at runtime. Instead the config change should " + - "be done via file system based config file and this view should ONLY " + - "be used to determine which options are supported." - ) - @interface Configuration { - - @AttributeDefinition( - name = "Repository Home Directory", + @Property( + label = "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"; + + "Defaults to the value of the framework property 'repository.home' or to 'repository' " + + "if that is neither specified." + ) + public static final String REPOSITORY_HOME_DIRECTORY = "repository.home"; - @AttributeDefinition( - name = "Mode", + @Property( + label = "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 ""; + "Default value is taken from the 'sun.arch.data.model' system property." + ) + public static final String MODE = "tarmk.mode"; - @AttributeDefinition( - name = "Maximum tar file size (MB)", + @Property( + intValue = DEFAULT_MAX_FILE_SIZE, + label = "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; + "Default value is '" + DEFAULT_MAX_FILE_SIZE + "'." + ) + public static final String SIZE = "tarmk.size"; - @AttributeDefinition( - name = "Segment cache size (MB)", + @Property( + intValue = DEFAULT_SEGMENT_CACHE_MB, + label = "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; + "Default value is '" + DEFAULT_SEGMENT_CACHE_MB + "'." + ) + public static final String SEGMENT_CACHE_SIZE = "segmentCache.size"; - @AttributeDefinition( - name = "String cache size (MB)", + @Property( + intValue = DEFAULT_STRING_CACHE_MB, + label = "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; + "Default value is '" + DEFAULT_STRING_CACHE_MB + "'." + ) + public static final String STRING_CACHE_SIZE = "stringCache.size"; - @AttributeDefinition( - name = "Template cache size (MB)", + @Property( + intValue = DEFAULT_TEMPLATE_CACHE_MB, + label = "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; + "Default value is '" + DEFAULT_TEMPLATE_CACHE_MB + "'." + ) + public static final String TEMPLATE_CACHE_SIZE = "templateCache.size"; - @AttributeDefinition( - name = "String deduplication cache size (#items)", + @Property( + intValue = DEFAULT_STRING_CACHE_SIZE_OSGi, + label = "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; + "Default value is '" + DEFAULT_STRING_CACHE_SIZE_OSGi + "'." + ) + public static final String STRING_DEDUPLICATION_CACHE_SIZE = "stringDeduplicationCache.size"; - @AttributeDefinition( - name = "Template deduplication cache size (#items)", + @Property( + intValue = DEFAULT_TEMPLATE_CACHE_SIZE_OSGi, + label = "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; + "Default value is '" + DEFAULT_TEMPLATE_CACHE_SIZE_OSGi + "'." + ) + public static final String TEMPLATE_DEDUPLICATION_CACHE_SIZE = "templateDeduplicationCache.size"; - @AttributeDefinition( - name = "Node deduplication cache size (#items)", + @Property( + intValue = DEFAULT_NODE_CACHE_SIZE_OSGi, + label = "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; + "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 + "'." + ) + public static final String NODE_DEDUPLICATION_CACHE_SIZE = "nodeDeduplicationCache.size"; - @AttributeDefinition( - name = "Pause compaction", + @Property( + boolValue = PAUSE_DEFAULT, + label = "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; + "Default value is '" + PAUSE_DEFAULT + "'." + ) + public static final String PAUSE_COMPACTION = "pauseCompaction"; - @AttributeDefinition( - name = "Compaction retries", + @Property( + intValue = RETRY_COUNT_DEFAULT, + label = "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; + "compacted commits. " + + "Default value is '" + RETRY_COUNT_DEFAULT + "'." + ) + public static final String COMPACTION_RETRY_COUNT = "compaction.retryCount"; - @AttributeDefinition( - name = "Force compaction timeout", + @Property( + intValue = FORCE_TIMEOUT_DEFAULT, + label = "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; + "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 + "'." + ) + public static final String COMPACTION_FORCE_TIMEOUT = "compaction.force.timeout"; - @AttributeDefinition( - name = "Garbage collection repository size threshold", + @Property( + longValue = SIZE_DELTA_ESTIMATION_DEFAULT, + label = "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; + "the number of bytes specified. " + + "Default value is '" + SIZE_DELTA_ESTIMATION_DEFAULT + "'." + ) + public static final String COMPACTION_SIZE_DELTA_ESTIMATION = "compaction.sizeDeltaEstimation"; - @AttributeDefinition( - name = "Disable estimation phase", + @Property( + boolValue = DISABLE_ESTIMATION_DEFAULT, + label = "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; + "Default value is '" + DISABLE_ESTIMATION_DEFAULT + "'." + ) + public static final String COMPACTION_DISABLE_ESTIMATION = "compaction.disableEstimation"; - @AttributeDefinition( - name = "Compaction retained generations", + @Property( + intValue = RETAINED_GENERATIONS_DEFAULT, + label = "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; + ) + public static final String RETAINED_GENERATIONS = "compaction.retainedGenerations"; - @AttributeDefinition( - name = "Compaction memory threshold", + @Property( + intValue = MEMORY_THRESHOLD_DEFAULT, + label = "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; + "which the compaction phase is canceled. 0 disables heap memory monitoring. " + + "Default value is '" + MEMORY_THRESHOLD_DEFAULT + "'." + ) + public static final String MEMORY_THRESHOLD = "compaction.memoryThreshold"; - @AttributeDefinition( - name = "Compaction progress log", + @Property( + longValue = GC_PROGRESS_LOG_DEFAULT, + label = "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; + "-1 disables progress logging. " + + "Default value is '" + GC_PROGRESS_LOG_DEFAULT + "'." + ) + public static final String GC_PROGRESS_LOG = "compaction.progressLog"; - @AttributeDefinition( - name = "Standby mode", + @Property( + boolValue = false, + label = "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; + "NodeStoreProvider instead. " + + "Default value is 'false'." + ) + public static final String STANDBY = "standby"; - @AttributeDefinition( - name = "Custom blob store", + @Property(boolValue = false, + label = "Custom blob store", description = "Boolean value indicating that a custom BlobStore is used for storing " + - "large binary values." - ) - boolean customBlobStore() default false; + "large binary values." + ) + public static final String CUSTOM_BLOB_STORE = "customBlobStore"; - @AttributeDefinition( - name = "Custom segment store", + @Property(boolValue = false, + label = "Custom segment store", description = "Boolean value indicating that a custom (non-tar) segment store is used" - ) - boolean customSegmentStore() default false; + ) + public static final String CUSTOM_SEGMENT_STORE = "customSegmentStore"; - @AttributeDefinition( - name = "Backup directory", + @Property( + label = "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; - - } + "Defaults to 'repository.home/segmentstore-backup'." + ) + public static final String BACKUP_DIRECTORY = "repository.backup.dir"; @Reference( - cardinality = ReferenceCardinality.OPTIONAL, - policy = ReferencePolicy.STATIC, - policyOption = ReferencePolicyOption.GREEDY, - target = ONLY_STANDALONE_TARGET + cardinality = ReferenceCardinality.OPTIONAL_UNARY, + policy = ReferencePolicy.STATIC, + policyOption = ReferencePolicyOption.GREEDY, + target = ONLY_STANDALONE_TARGET ) private volatile BlobStore blobStore; @Reference( - cardinality = ReferenceCardinality.OPTIONAL, - policy = ReferencePolicy.STATIC, - policyOption = ReferencePolicyOption.GREEDY + cardinality = ReferenceCardinality.OPTIONAL_UNARY, + policy = ReferencePolicy.STATIC, + policyOption = ReferencePolicyOption.GREEDY ) private volatile SegmentNodeStorePersistence segmentStore; @Reference private StatisticsProvider statisticsProvider = StatisticsProvider.NOOP; + + private Closer closer; - private final Closer closer = Closer.create(); + /** + * Blob modified before this time duration would be considered for Blob GC + */ + static final long DEFAULT_BLOB_GC_MAX_AGE = 24 * 60 * 60; + + @Property(longValue = DEFAULT_BLOB_GC_MAX_AGE, + label = "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 + "'." + ) + public static final String PROP_BLOB_GC_MAX_AGE = "blobGcMaxAgeInSecs"; + + /** + * Default interval for taking snapshots of locally tracked blob ids. + */ + static final long DEFAULT_BLOB_SNAPSHOT_INTERVAL = 12 * 60 * 60; + + @Property(longValue = DEFAULT_BLOB_SNAPSHOT_INTERVAL, + label = "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 + "'." + ) + public static final String PROP_BLOB_SNAPSHOT_INTERVAL = "blobTrackSnapshotIntervalInSecs"; @Activate - public void activate(ComponentContext context, Configuration configuration) throws IOException { + public void activate(ComponentContext context) throws IOException { + Configuration configuration = new Configuration(context); + if (blobStore == null && configuration.hasCustomBlobStore()) { + log.info("BlobStore enabled. SegmentNodeStore will be initialized once the blob " + + "store becomes available"); + return; + } + if (segmentStore == null && configuration.hasCustomSegmentStore()) { + log.info("customSegmentStore enabled. SegmentNodeStore will be initialized once the custom segment " + + "store becomes available"); + return; + } + closer = Closer.create(); OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext()); - registerSegmentStore(context, configuration, blobStore, segmentStore, statisticsProvider, closer, whiteboard, log); + registerSegmentStore(context, blobStore, segmentStore, statisticsProvider, closer, whiteboard, null, true); } - private static SegmentNodeStore registerSegmentStore( - ComponentContext context, - Configuration configuration, - BlobStore blobStore, - SegmentNodeStorePersistence segmentStore, - StatisticsProvider statisticsProvider, - Closer closer, - Whiteboard whiteboard, - Logger logger + /** + * Configures and registers a new SegmentNodeStore instance together will + * all required components. Anything that must be disposed of (like + * registered services or MBeans) will be registered via the + * {@code registration} parameter. + * + * @param context An instance of {@link ComponentContext}. + * @param blobStore An instance of {@link BlobStore}. It can be + * {@code null}. + * @param segmentStore An instance of {@link SegmentNodeStorePersistence}. It can be + * {@code null}. + * @param statisticsProvider An instance of {@link StatisticsProvider}. + * @param closer An instance of {@link Closer}. It will be used + * to track every registered service or + * component. + * @param whiteboard An instance of {@link Whiteboard}. It will be + * used to register services in the OSGi + * framework. + * @param role The role of this component. It can be {@code + * null}. + * @param descriptors Determines if repository descriptors related to + * discovery services should be registered. + * @return A configured {@link SegmentNodeStore}, or {@code null} if the + * setup failed. + * @throws IOException In case an unrecoverable error occurs. + */ + static SegmentNodeStore registerSegmentStore( + @Nonnull ComponentContext context, + @Nullable BlobStore blobStore, + @Nullable SegmentNodeStorePersistence segmentStore, + @Nonnull StatisticsProvider statisticsProvider, + @Nonnull Closer closer, + @Nonnull Whiteboard whiteboard, + @Nullable String role, + boolean descriptors ) throws IOException { - return SegmentNodeStoreRegistrar.registerSegmentNodeStore(new SegmentNodeStoreRegistrar.Configuration() { - - 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")); + Configuration configuration = new Configuration(context, role); + Closeables closeables = new Closeables(closer); + Registrations registrations = new Registrations(whiteboard, role); + + // Listen for GCMonitor services + GCMonitor gcMonitor = GCMonitor.EMPTY; + + if (configuration.isPrimarySegmentStore()) { + GCMonitorTracker tracker = new GCMonitorTracker(); + tracker.start(whiteboard); + closeables.add(tracker); + gcMonitor = tracker; + } + + // Create the gc options + if (configuration.getCompactionGainThreshold() != null) { + log.warn("Detected deprecated flag 'compaction.gainThreshold'. " + + "Please use 'compaction.sizeDeltaEstimation' instead and " + + "'compaction.disableEstimation' to disable estimation."); + } + if (configuration.getRetainedGenerations() != RETAINED_GENERATIONS_DEFAULT) { + log.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(configuration.getPauseCompaction(), configuration.getRetryCount(), configuration.getForceCompactionTimeout()) + .setGcSizeDeltaEstimation(configuration.getSizeDeltaEstimation()) + .setMemoryThreshold(configuration.getMemoryThreshold()) + .setEstimationDisabled(configuration.getDisableEstimation()) + .setGCLogInterval(configuration.getGCProcessLog()); + if (configuration.isStandbyInstance()) { + gcOptions.setRetainedGenerations(1); + } + + // Build the FileStore + FileStoreBuilder builder = fileStoreBuilder(configuration.getSegmentDirectory()) + .withSegmentCacheSize(configuration.getSegmentCacheSize()) + .withStringCacheSize(configuration.getStringCacheSize()) + .withTemplateCacheSize(configuration.getTemplateCacheSize()) + .withStringDeduplicationCacheSize(configuration.getStringDeduplicationCacheSize()) + .withTemplateDeduplicationCacheSize(configuration.getTemplateDeduplicationCacheSize()) + .withNodeDeduplicationCacheSize(configuration.getNodeDeduplicationCacheSize()) + .withMaxFileSize(configuration.getMaxFileSize()) + .withMemoryMapping(configuration.getMemoryMapping()) + .withGCMonitor(gcMonitor) + .withIOMonitor(new MetricsIOMonitor(statisticsProvider)) + .withIOLogging(LoggerFactory.getLogger(SegmentTarReader.class)) + .withStatisticsProvider(statisticsProvider) + .withGCOptions(gcOptions); + + if (configuration.hasCustomBlobStore() && blobStore != null) { + log.info("Initializing SegmentNodeStore with BlobStore [{}]", blobStore); + builder.withBlobStore(blobStore); + } + + if (configuration.hasCustomSegmentStore() && segmentStore != null) { + log.info("Initializing SegmentNodeStore with custom persistence [{}]", segmentStore); + builder.withCustomPersistence(segmentStore); + } + + if (configuration.isStandbyInstance()) { + builder.withSnfeListener(IGNORE_SNFE); + } + + final FileStore store; + try { + store = builder.build(); + } catch (InvalidFileStoreVersionException e) { + log.error("The storage format is not compatible with this version of Oak Segment Tar", e); + return null; + } + // store should be closed last + closeables.add(store); + + // Listen for Executor services on the whiteboard + + WhiteboardExecutor executor = new WhiteboardExecutor(); + executor.start(whiteboard); + closeables.add(executor); + + // Expose stats about the segment cache + + CacheStatsMBean segmentCacheStats = addRoleToName(store.getSegmentCacheStats(), role); + closeables.add(registrations.registerMBean( + CacheStatsMBean.class, + segmentCacheStats, + CacheStats.TYPE, + segmentCacheStats.getName() + )); + + // Expose stats about the string and template caches + + CacheStatsMBean stringCacheStats = addRoleToName(store.getStringCacheStats(), role); + closeables.add(registrations.registerMBean( + CacheStatsMBean.class, + stringCacheStats, + CacheStats.TYPE, + stringCacheStats.getName() + )); + + CacheStatsMBean templateCacheStats = addRoleToName(store.getTemplateCacheStats(), role); + closeables.add(registrations.registerMBean( + CacheStatsMBean.class, + templateCacheStats, + CacheStats.TYPE, + templateCacheStats.getName() + )); + + WriterCacheManager cacheManager = builder.getCacheManager(); + CacheStatsMBean stringDeduplicationCacheStats = addRoleToName(cacheManager.getStringCacheStats(), role); + if (stringDeduplicationCacheStats != null) { + closeables.add(registrations.registerMBean( + CacheStatsMBean.class, + stringDeduplicationCacheStats, + CacheStats.TYPE, + stringDeduplicationCacheStats.getName() + )); + } + + CacheStatsMBean templateDeduplicationCacheStats = addRoleToName(cacheManager.getTemplateCacheStats(), role); + if (templateDeduplicationCacheStats != null) { + closeables.add(registrations.registerMBean( + CacheStatsMBean.class, + templateDeduplicationCacheStats, + CacheStats.TYPE, + templateDeduplicationCacheStats.getName() + )); + } + + CacheStatsMBean nodeDeduplicationCacheStats = addRoleToName(cacheManager.getNodeCacheStats(), role); + if (nodeDeduplicationCacheStats != null) { + closeables.add(registrations.registerMBean( + CacheStatsMBean.class, + nodeDeduplicationCacheStats, + CacheStats.TYPE, + nodeDeduplicationCacheStats.getName() + )); + } + + // Expose an MBean to managing and monitoring garbage collection + final FileStoreGCMonitor monitor = new FileStoreGCMonitor(Clock.SIMPLE); + closeables.add(registrations.register( + GCMonitor.class, + monitor + )); + if (!configuration.isStandbyInstance()) { + closeables.add(registrations.registerMBean( + SegmentRevisionGC.class, + new SegmentRevisionGCMBean(store, gcOptions, monitor), + SegmentRevisionGC.TYPE, + "Segment node store revision garbage collection" + )); + } + + Runnable cancelGC = new Runnable() { + + @Override + public void run() { + store.cancelGC(); + } + + }; + Supplier<String> statusMessage = new Supplier<String>() { + + @Override + public String get() { + return monitor.getStatus(); + } + + }; + closeables.add(registrations.registerMBean( + RevisionGCMBean.class, + new RevisionGC(store.getGCRunner(), cancelGC, statusMessage, executor), + RevisionGCMBean.TYPE, + "Revision garbage collection" + )); + + // Expose statistics about the FileStore + + closeables.add(registrations.registerMBean( + FileStoreStatsMBean.class, + store.getStats(), + FileStoreStatsMBean.TYPE, + "FileStore statistics" + )); + + // register segment node store + + SegmentNodeStore.SegmentNodeStoreBuilder segmentNodeStoreBuilder = SegmentNodeStoreBuilders.builder(store).withStatisticsProvider(statisticsProvider); + if (configuration.isStandbyInstance() || !configuration.isPrimarySegmentStore()) { + segmentNodeStoreBuilder.dispatchChanges(false); + } + SegmentNodeStore segmentNodeStore = segmentNodeStoreBuilder.build(); + + if (configuration.isPrimarySegmentStore()) { + ObserverTracker observerTracker = new ObserverTracker(segmentNodeStore); + observerTracker.start(context.getBundleContext()); + closeables.add(observerTracker); + } + + if (configuration.isPrimarySegmentStore()) { + closeables.add(registrations.registerMBean( + CheckpointMBean.class, + new SegmentCheckpointMBean(segmentNodeStore), + CheckpointMBean.TYPE, + "Segment node store checkpoint management" + )); + } + + if (descriptors) { + // 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 + ); + closeables.add(registrations.register(Descriptors.class, clusterIdDesc)); + // Register "discovery lite" descriptors + closeables.add(registrations.register(Descriptors.class, new SegmentDiscoveryLiteDescriptors(segmentNodeStore))); + } + + // If a shared data store register the repo id in the data store + if (configuration.isPrimarySegmentStore() && isShared(blobStore)) { + SharedDataStore sharedDataStore = (SharedDataStore) blobStore; + try { + sharedDataStore.addMetadataRecord(new ByteArrayInputStream(new byte[0]), SharedStoreRecordType.REPOSITORY.getNameFromId(getOrCreateId(segmentNodeStore))); + } catch (Exception e) { + throw new IOException("Could not register a unique repositoryId", e); + } + if (blobStore instanceof BlobTrackingStore) { + BlobTrackingStore trackingStore = (BlobTrackingStore) blobStore; + if (trackingStore.getTracker() != null) { + trackingStore.getTracker().close(); } - return mode; + trackingStore.addTracker(new BlobIdTracker(configuration.getRepositoryHome(), getOrCreateId(segmentNodeStore), configuration.getBlobSnapshotInterval(), sharedDataStore)); } + } - int getCacheSize(String name, int otherwise) { - Integer size = Integer.getInteger(name); - if (size != null) { - return size; - } - return otherwise; - } + if (configuration.isPrimarySegmentStore() && blobStore instanceof GarbageCollectableBlobStore) { + BlobGarbageCollector gc = new MarkSweepGarbageCollector( + new SegmentBlobReferenceRetriever(store), + (GarbageCollectableBlobStore) blobStore, + executor, + TimeUnit.SECONDS.toMillis(configuration.getBlobGcMaxAge()), + getOrCreateId(segmentNodeStore), + whiteboard, + statisticsProvider + ); + closeables.add(registrations.registerMBean( + BlobGCMBean.class, + new BlobGC(gc, executor), + BlobGCMBean.TYPE, + "Segment node store blob garbage collection" + )); + } + + // Expose an MBean for backup/restore operations + + closeables.add(registrations.registerMBean( + FileStoreBackupRestoreMBean.class, + new FileStoreBackupRestoreImpl( + segmentNodeStore, + store.getRevisions(), + store.getReader(), + configuration.getBackupDirectory(), + executor + ), + FileStoreBackupRestoreMBean.TYPE, + "Segment node store backup/restore" + )); + + // Expose statistics about the SegmentNodeStore + + closeables.add(registrations.registerMBean( + SegmentNodeStoreStatsMBean.class, + segmentNodeStore.getStats(), + SegmentNodeStoreStatsMBean.TYPE, + "SegmentNodeStore statistics" + )); + + if (configuration.isPrimarySegmentStore()) { + log.info("Primary SegmentNodeStore initialized"); + } else { + log.info("Secondary SegmentNodeStore initialized, role={}", role); + } + + // Register a factory service to expose the FileStore + closeables.add(registrations.register( + SegmentStoreProvider.class, + new DefaultSegmentStoreProvider(store) + )); + + if (configuration.isStandbyInstance()) { + return segmentNodeStore; + } + + if (configuration.isPrimarySegmentStore()) { + Map<String, Object> props = new HashMap<String, Object>(); + props.put(Constants.SERVICE_PID, SegmentNodeStore.class.getName()); + props.put("oak.nodestore.description", new String[] {"nodeStoreType=segment"}); + closeables.add(registrations.register(NodeStore.class, segmentNodeStore, props)); + } - @Override - public boolean isPrimarySegmentStore() { - return true; - } + return segmentNodeStore; + } - @Override - public boolean isSecondarySegmentStore() { - return false; - } + @Deactivate + public void deactivate() { + closeQuietly(closer); + closer = null; + } + private static CacheStatsMBean addRoleToName(CacheStatsMBean cacheStatsMBean, String role) { + return new CacheStatsMBeanWrapper(cacheStatsMBean) { @Override - public boolean isStandbyInstance() { - return configuration.standby(); + public String getName() { + return RoleUtils.maybeAppendRole(super.getName(), role); } + }; + } +} - @Override - public String getRole() { - return null; - } +/** + * Encapsulates a {@link Closer} and makes it easier to track the lifecycle + * of entities that can be disposed. + */ +class Closeables implements Closeable { - @Override - public int getRetainedGenerations() { - return configuration.compaction_retainedGenerations(); - } + private final Closer closer; - @Override - public int getDefaultRetainedGenerations() { - return RETAINED_GENERATIONS_DEFAULT; - } + Closeables(Closer closer) { + this.closer = closer; + } - @Override - public boolean getPauseCompaction() { - return configuration.pauseCompaction(); - } + void add(Closeable c) { + closer.register(c); + } - @Override - public int getRetryCount() { - return configuration.compaction_retryCount(); - } + void add(final AbstractServiceTracker<?> t) { + add(new Closeable() { @Override - public int getForceCompactionTimeout() { - return configuration.compaction_force_timeout(); + public void close() { + t.stop(); } - @Override - public long getSizeDeltaEstimation() { - return configuration.compaction_sizeDeltaEstimation(); - } + }); + } - @Override - public int getMemoryThreshold() { - return configuration.compaction_memoryThreshold(); - } + void add(final Registration r) { + add(new Closeable() { @Override - public boolean getDisableEstimation() { - return configuration.compaction_disableEstimation(); + public void close() { + r.unregister(); } - @Override - public long getGCProcessLog() { - return configuration.compaction_progressLog(); - } + }); + } - @Override - public File getSegmentDirectory() { - return new File(getRepositoryHome(), "segmentstore"); - } + void add(final ObserverTracker t) { + add(new Closeable() { @Override - public int getSegmentCacheSize() { - Integer size = Integer.getInteger("segmentCache.size"); - if (size != null) { - return size; - } - return configuration.segmentCache_size(); + public void close() { + t.stop(); } - @Override - public int getStringCacheSize() { - return getCacheSize("stringCache.size", configuration.stringCache_size()); - } + }); + } - @Override - public int getTemplateCacheSize() { - Integer size = Integer.getInteger("templateCache.size"); - if (size != null) { - return size; - } - return configuration.templateCache_size(); - } + @Override + public void close() throws IOException { + closer.close(); + } - @Override - public int getStringDeduplicationCacheSize() { - Integer size = Integer.getInteger("stringDeduplicationCache.size"); - if (size != null) { - return size; - } - return configuration.stringDeduplicationCache_size(); - } +} - @Override - public int getTemplateDeduplicationCacheSize() { - Integer size = Integer.getInteger("templateDeduplicationCache.size"); - if (size != null) { - return size; - } - return configuration.templateDeduplicationCache_size(); - } +/** + * Allows simple access to the configuration of this component. Provides + * default values for unspecified properties and type conversion. + */ +class Configuration { - @Override - public int getNodeDeduplicationCacheSize() { - Integer size = Integer.getInteger("nodeDeduplicationCache.size"); - if (size != null) { - return roundToNextPowerOfTwo(size); - } - return roundToNextPowerOfTwo(configuration.nodeDeduplicationCache_size()); - } + private static int roundToNextPowerOfTwo(int size) { + return 1 << (32 - Integer.numberOfLeadingZeros(Math.max(0, size - 1))); + } - @Override - public int getMaxFileSize() { - return configuration.tarmk_size(); - } + private final ComponentContext context; - @Override - public boolean getMemoryMapping() { - return getMode().equals("64"); - } + private final String role; - @Override - public boolean hasCustomBlobStore() { - return configuration.customBlobStore(); - } + Configuration(ComponentContext context) { + this(context, null); + } - @Override - public boolean hasCustomSegmentStore() { - return configuration.customSegmentStore(); - } + Configuration(ComponentContext context, String role) { + this.context = context; + this.role = role; + } - @Override - public boolean registerDescriptors() { - return true; - } + String property(String name) { + return lookupConfigurationThenFramework(context, name); + } - @Override - public String getRepositoryHome() { - return configuration.repository_home(); - } + /** + * Chooses repository home directory name based on <b>repository.home</b> + * property, defaulting to <b>repository</b> if property is not set. + * + * @return repository home directory name. + */ + String getRepositoryHome() { + String root = property(REPOSITORY_HOME_DIRECTORY); + if (isNullOrEmpty(root)) { + return "repository"; + } + return root; + } - @Override - public long getBlobSnapshotInterval() { - return configuration.blobTrackSnapshotIntervalInSecs(); - } + /** + * Creates a new sub-directory relative to {@link #getRepositoryHome()} for + * storing segments. + * + * @return directory for storing segments. + */ + File getSegmentDirectory() { + return new File(getRepositoryHome(), appendRole("segmentstore")); + } - @Override - public long getBlobGcMaxAge() { - return configuration.blobGcMaxAgeInSecs(); - } + /** + * Creates a new sub-directory relative to {@link #getRepositoryHome()} for + * storing repository backups. + * + * @return directory for storing repository backups. + */ + File getBackupDirectory() { + String backupDirectory = property(BACKUP_DIRECTORY); + if (backupDirectory != null) { + return new File(backupDirectory); + } + return new File(getRepositoryHome(), appendRole("segmentstore-backup")); + } - @Override - public File getBackupDirectory() { - String backupDirectory = configuration.repository_backup_dir(); - if (isNullOrEmpty(backupDirectory)) { - return new File(getRepositoryHome(), "segmentstore-backup"); - } - return new File(backupDirectory); - } + int getSegmentCacheSize() { + return toInteger(getCacheSize(SEGMENT_CACHE_SIZE), DEFAULT_SEGMENT_CACHE_MB); + } - @Override - public Whiteboard getWhiteboard() { - return whiteboard; - } + int getStringCacheSize() { + return toInteger(getCacheSize(STRING_CACHE_SIZE), DEFAULT_STRING_CACHE_MB); + } - @Override - public Closer getCloser() { - return closer; - } + int getTemplateCacheSize() { + return toInteger(getCacheSize(TEMPLATE_CACHE_SIZE), DEFAULT_TEMPLATE_CACHE_MB); + } - @Override - public Logger getLogger() { - return logger; - } + int getStringDeduplicationCacheSize() { + return toInteger(getCacheSize(STRING_DEDUPLICATION_CACHE_SIZE), DEFAULT_STRING_CACHE_SIZE_OSGi); + } - @Override - public StatisticsProvider getStatisticsProvider() { - return statisticsProvider; - } + int getTemplateDeduplicationCacheSize() { + return toInteger(getCacheSize(TEMPLATE_DEDUPLICATION_CACHE_SIZE), DEFAULT_TEMPLATE_CACHE_SIZE_OSGi); + } - @Override - public BlobStore getBlobStore() { - return blobStore; - } + int getNodeDeduplicationCacheSize() { + return roundToNextPowerOfTwo(toInteger(getCacheSize(NODE_DEDUPLICATION_CACHE_SIZE), DEFAULT_NODE_CACHE_SIZE_OSGi)); + } - @Override - public SegmentNodeStorePersistence getSegmentNodeStorePersistence() { - return segmentStore; - } + boolean getPauseCompaction() { + return toBoolean(property(PAUSE_COMPACTION), PAUSE_DEFAULT); + } - @Override - public BundleContext getBundleContext() { - return context.getBundleContext(); - } + int getRetryCount() { + return toInteger(property(COMPACTION_RETRY_COUNT), RETRY_COUNT_DEFAULT); + } - }); + int getForceCompactionTimeout() { + return toInteger(property(COMPACTION_FORCE_TIMEOUT), FORCE_TIMEOUT_DEFAULT); } - @Deactivate - public void deactivate() { - closeQuietly(closer); + int getRetainedGenerations() { + return toInteger(property(RETAINED_GENERATIONS), RETAINED_GENERATIONS_DEFAULT); + } + + long getSizeDeltaEstimation() { + return toLong(property(COMPACTION_SIZE_DELTA_ESTIMATION), SIZE_DELTA_ESTIMATION_DEFAULT); + } + + int getMemoryThreshold() { + return toInteger(property(MEMORY_THRESHOLD), MEMORY_THRESHOLD_DEFAULT); + } + + boolean getDisableEstimation() { + return toBoolean(property(COMPACTION_DISABLE_ESTIMATION), DISABLE_ESTIMATION_DEFAULT); + } + + String getCompactionGainThreshold() { + return property("compaction.gainThreshold"); + } + + long getGCProcessLog() { + return toLong(property(GC_PROGRESS_LOG), GC_PROGRESS_LOG_DEFAULT); + } + + int getMaxFileSize() { + return toInteger(property(SIZE), DEFAULT_MAX_FILE_SIZE); + } + + String getMode() { + String mode = property(MODE); + if (mode != null) { + return mode; + } + return System.getProperty(MODE, System.getProperty("sun.arch.data.model", "32")); + } + + boolean getMemoryMapping() { + return getMode().equals("64"); + } + + long getBlobSnapshotInterval() { + return toLong(property(PROP_BLOB_SNAPSHOT_INTERVAL), DEFAULT_BLOB_SNAPSHOT_INTERVAL); + } + + boolean isStandbyInstance() { + return toBoolean(property(STANDBY), false); + } + + boolean hasCustomBlobStore() { + return toBoolean(property(CUSTOM_BLOB_STORE), false); + } + + boolean hasCustomSegmentStore() { + return toBoolean(property(CUSTOM_SEGMENT_STORE), false); + } + + long getBlobGcMaxAge() { + return toLong(property(PROP_BLOB_GC_MAX_AGE), DEFAULT_BLOB_GC_MAX_AGE); + } + + boolean isPrimarySegmentStore() { + return role == null; + } + + private String appendRole(String name) { + if (role == null) { + return name; + } else { + return name + "-" + role; + } + } + + private String getCacheSize(String propertyName) { + String cacheSize = property(propertyName); + if (cacheSize != null) { + return cacheSize; + } + return System.getProperty(propertyName); + } + +} + +/** + * Performs registrations of services and MBean in a uniform way. Augments + * the metadata of services and MBeans with an optionally provided role + * name. + */ +class Registrations { + + private final Whiteboard whiteboard; + + private final String role; + + Registrations(Whiteboard whiteboard, String role) { + this.whiteboard = whiteboard; + this.role = role; + } + + <T> Registration registerMBean(Class<T> clazz, T bean, String type, String name) { + return registerMBean(clazz, bean, type, name, new HashMap<String, String>()); + } + + <T> Registration registerMBean(Class<T> clazz, T bean, String type, String name, Map<String, String> attributes) { + return WhiteboardUtils.registerMBean(whiteboard, clazz, bean, type, maybeAppendRole(name), maybePutRoleAttribute(attributes)); + } + + <T> Registration register(Class<T> clazz, T service) { + return register(clazz, service, new HashMap<String, Object>()); + } + + <T> Registration register(Class<T> clazz, T service, Map<String, Object> properties) { + return whiteboard.register(clazz, service, maybePutRoleProperty(properties)); + } + + private String maybeAppendRole(String name) { + return RoleUtils.maybeAppendRole(name, role); + } + + private String jmxRole() { + return role.replaceAll(":", "-"); + } + + private Map<String, String> maybePutRoleAttribute(Map<String, String> attributes) { + if (role != null) { + attributes.put("role", jmxRole()); + } + return attributes; + } + + private Map<String, Object> maybePutRoleProperty(Map<String, Object> attributes) { + if (role != null) { + attributes.put("role", role); + } + return attributes; } -} \ No newline at end of file +}
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceDeprecationError.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceDeprecationError.java?rev=1836149&r1=1836148&r2=1836149&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceDeprecationError.java (original) +++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreServiceDeprecationError.java Wed Jul 18 07:42:54 2018 @@ -17,9 +17,9 @@ package org.apache.jackrabbit.oak.segment.osgi; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.ConfigurationPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,8 +30,8 @@ import org.slf4j.LoggerFactory; * detected problem and hinting at a possible solution. */ @Component( - configurationPolicy = ConfigurationPolicy.REQUIRE, - configurationPid = "org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStoreService" + policy = ConfigurationPolicy.REQUIRE, + configurationPid = "org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStoreService" ) public class SegmentNodeStoreServiceDeprecationError { Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceDeprecationError.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceDeprecationError.java?rev=1836149&r1=1836148&r2=1836149&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceDeprecationError.java (original) +++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/osgi/StandbyStoreServiceDeprecationError.java Wed Jul 18 07:42:54 2018 @@ -16,9 +16,9 @@ */ package org.apache.jackrabbit.oak.segment.osgi; -import org.osgi.service.component.annotations.Activate; -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.ConfigurationPolicy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,8 +29,8 @@ import org.slf4j.LoggerFactory; * detected problem and hinting at a possible solution. */ @Component( - configurationPolicy = ConfigurationPolicy.REQUIRE, - configurationPid = "org.apache.jackrabbit.oak.plugins.segment.standby.store.StandbyStoreService" + policy = ConfigurationPolicy.REQUIRE, + configurationPid = "org.apache.jackrabbit.oak.plugins.segment.standby.store.StandbyStoreService" ) public class StandbyStoreServiceDeprecationError { Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java?rev=1836149&r1=1836148&r2=1836149&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java (original) +++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java Wed Jul 18 07:42:54 2018 @@ -17,15 +17,25 @@ package org.apache.jackrabbit.oak.segment.standby.store; -import static org.osgi.service.component.annotations.ReferencePolicy.STATIC; -import static org.osgi.service.component.annotations.ReferencePolicyOption.GREEDY; +import static java.lang.String.valueOf; +import static org.apache.felix.scr.annotations.ReferencePolicy.STATIC; +import static org.apache.felix.scr.annotations.ReferencePolicyOption.GREEDY; +import java.io.Closeable; import java.io.File; import java.util.Dictionary; import java.util.Hashtable; import com.google.common.base.StandardSystemProperty; 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.PropertyOption; +import org.apache.felix.scr.annotations.Reference; +import org.apache.jackrabbit.oak.commons.PropertiesUtil; import org.apache.jackrabbit.oak.segment.SegmentStore; import org.apache.jackrabbit.oak.segment.SegmentStoreProvider; import org.apache.jackrabbit.oak.segment.file.FileStore; @@ -33,99 +43,71 @@ import org.apache.jackrabbit.oak.segment import org.apache.jackrabbit.oak.segment.standby.server.StandbyServerSync; import org.osgi.framework.ServiceRegistration; 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.metatype.annotations.AttributeDefinition; -import org.osgi.service.metatype.annotations.Designate; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; -import org.osgi.service.metatype.annotations.Option; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@Component(configurationPolicy = ConfigurationPolicy.REQUIRE) -@Designate(ocd = StandbyStoreService.Configuration.class) +@Property(name = "org.apache.sling.installer.configuration.persist", label = "Persist configuration", description = "Must be always disabled to avoid storing the configuration in the repository", boolValue = false) +@Component(metatype = true, policy = ConfigurationPolicy.REQUIRE) public class StandbyStoreService { - private static final Logger log = LoggerFactory.getLogger(StandbyStoreService.class); + private final Logger log = LoggerFactory.getLogger(getClass()); - private static final int BLOB_CHUNK_SIZE = Integer.getInteger("oak.standby.blob.chunkSize", 1024 * 1024); + private static final String MODE_PRIMARY = "primary"; - @ObjectClassDefinition( - name = "Apache Jackrabbit Oak Segment Tar Cold Standby Service", - description = "Provides continuous backups of repositories based on Segment Tar" - ) - @interface Configuration { - - @AttributeDefinition( - name = "Persist configuration", - description = "Must be always disabled to avoid storing the configuration in the repository" - ) - boolean org_apache_sling_installer_configuration_persist() default false; - - @AttributeDefinition( - name = "Mode", - description = "TarMK Cold Standby mode (primary or standby)", - options = { - @Option(label = "primary", value = "primary"), - @Option(label = "standby", value = "standby")} - ) - String mode() default "primary"; - - @AttributeDefinition( - name = "Port", - description = "TCP/IP port to use" - ) - int port() default 8023; - - @AttributeDefinition( - name = "Primary Host", - description = "Primary host (standby mode only)" - ) - String primary_host() default "127.0.0.1"; - - @AttributeDefinition( - name = "Sync interval (seconds)", - description = "Sync interval in seconds (standby mode only)" - ) - int interval() default 5; - - @AttributeDefinition( - name = "Allowed IP-Ranges", - description = "Accept incoming requests for these host names and IP-ranges only (primary mode only)", - cardinality = Integer.MAX_VALUE - ) - String[] primary_allowed$_$client$_$ip$_$ranges() default {}; - - @AttributeDefinition( - name = "Secure", - description = "Use secure connections" - ) - boolean secure() default false; - - @AttributeDefinition( - name = "Standby Read Timeout", - description = "Timeout for requests issued from the standby instance in milliseconds" - ) - int standby_readtimeout() default 60000; - - @AttributeDefinition( - name = "Standby Automatic Cleanup", - description = "Call the cleanup method when the root segment Garbage Collector (GC) generation number increases" - ) - boolean standby_autoclean() default true; + private static final String MODE_STANDBY = "standby"; - } + public static final String MODE_DEFAULT = MODE_PRIMARY; + + @Property(options = { + @PropertyOption(name = MODE_PRIMARY, value = MODE_PRIMARY), + @PropertyOption(name = MODE_STANDBY, value = MODE_STANDBY)}, + value = MODE_DEFAULT) + public static final String MODE = "mode"; + + public static final int PORT_DEFAULT = 8023; + + @Property(intValue = PORT_DEFAULT) + public static final String PORT = "port"; + + public static final String PRIMARY_HOST_DEFAULT = "127.0.0.1"; + + @Property(value = PRIMARY_HOST_DEFAULT) + public static final String PRIMARY_HOST = "primary.host"; + + public static final int INTERVAL_DEFAULT = 5; + + @Property(intValue = INTERVAL_DEFAULT) + public static final String INTERVAL = "interval"; + + public static final String[] ALLOWED_CLIENT_IP_RANGES_DEFAULT = new String[] {}; + + @Property(cardinality = Integer.MAX_VALUE) + public static final String ALLOWED_CLIENT_IP_RANGES = "primary.allowed-client-ip-ranges"; + + public static final boolean SECURE_DEFAULT = false; + + @Property(boolValue = SECURE_DEFAULT) + public static final String SECURE = "secure"; + + public static final int READ_TIMEOUT_DEFAULT = 60000; + + @Property(intValue = READ_TIMEOUT_DEFAULT) + public static final String READ_TIMEOUT = "standby.readtimeout"; + + public static final boolean AUTO_CLEAN_DEFAULT = true; + + @Property(boolValue = AUTO_CLEAN_DEFAULT) + public static final String AUTO_CLEAN = "standby.autoclean"; @Reference(policy = STATIC, policyOption = GREEDY) private SegmentStoreProvider storeProvider = null; + + private static final int BLOB_CHUNK_SIZE = Integer.getInteger("oak.standby.blob.chunkSize", 1024 * 1024); private final Closer closer = Closer.create(); @Activate - private void activate(ComponentContext context, Configuration config) { + private void activate(ComponentContext context) { SegmentStore segmentStore = storeProvider.getSegmentStore(); if (!(segmentStore instanceof FileStore)) { @@ -134,15 +116,15 @@ public class StandbyStoreService { FileStore fileStore = (FileStore) segmentStore; - String mode = config.mode(); + String mode = valueOf(context.getProperties().get(MODE)); - if (mode.equals("primary")) { - bootstrapMaster(config, fileStore); + if (MODE_PRIMARY.equals(mode)) { + bootstrapMaster(context, fileStore); return; } - if (mode.equals("standby")) { - bootstrapSlave(context, config, fileStore); + if (MODE_STANDBY.equals(mode)) { + bootstrapSlave(context, fileStore); return; } @@ -154,10 +136,11 @@ public class StandbyStoreService { closer.close(); } - private void bootstrapMaster(Configuration config, FileStore fileStore) { - int port = config.port(); - String[] ranges = config.primary_allowed$_$client$_$ip$_$ranges(); - boolean secure = config.secure(); + private void bootstrapMaster(ComponentContext context, FileStore fileStore) { + Dictionary<?, ?> props = context.getProperties(); + int port = PropertiesUtil.toInteger(props.get(PORT), PORT_DEFAULT); + String[] ranges = PropertiesUtil.toStringArray(props.get(ALLOWED_CLIENT_IP_RANGES), ALLOWED_CLIENT_IP_RANGES_DEFAULT); + boolean secure = PropertiesUtil.toBoolean(props.get(SECURE), SECURE_DEFAULT); StandbyServerSync standbyServerSync = new StandbyServerSync(port, fileStore, BLOB_CHUNK_SIZE, ranges, secure); closer.register(standbyServerSync); @@ -166,13 +149,14 @@ public class StandbyStoreService { log.info("Started primary on port {} with allowed IP ranges {}", port, ranges); } - private void bootstrapSlave(ComponentContext context, Configuration config, FileStore fileStore) { - int port = config.port(); - long interval = config.interval(); - String host = config.primary_host(); - boolean secure = config.secure(); - int readTimeout = config.standby_readtimeout(); - boolean clean = config.standby_autoclean(); + private void bootstrapSlave(ComponentContext context, FileStore fileStore) { + Dictionary<?, ?> props = context.getProperties(); + int port = PropertiesUtil.toInteger(props.get(PORT), PORT_DEFAULT); + long interval = PropertiesUtil.toInteger(props.get(INTERVAL), INTERVAL_DEFAULT); + String host = PropertiesUtil.toString(props.get(PRIMARY_HOST), PRIMARY_HOST_DEFAULT); + boolean secure = PropertiesUtil.toBoolean(props.get(SECURE), SECURE_DEFAULT); + int readTimeout = PropertiesUtil.toInteger(props.get(READ_TIMEOUT), READ_TIMEOUT_DEFAULT); + boolean clean = PropertiesUtil.toBoolean(props.get(AUTO_CLEAN), AUTO_CLEAN_DEFAULT); StandbyClientSync standbyClientSync = new StandbyClientSync(host, port, fileStore, secure, readTimeout, clean, new File(StandardSystemProperty.JAVA_IO_TMPDIR.value())); closer.register(standbyClientSync); @@ -181,9 +165,20 @@ public class StandbyStoreService { dictionary.put("scheduler.period", interval); dictionary.put("scheduler.concurrent", false); ServiceRegistration registration = context.getBundleContext().registerService(Runnable.class.getName(), standbyClientSync, dictionary); - closer.register(registration::unregister); + closer.register(asCloseable(registration)); log.info("Started standby on port {} with {}s sync frequency", port, interval); } + private static Closeable asCloseable(final ServiceRegistration r) { + return new Closeable() { + + @Override + public void close() { + r.unregister(); + } + + }; + } + } Added: jackrabbit/oak/trunk/oak-segment-tar/src/main/resources/OSGI-INF/l10n/metatype.properties URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/resources/OSGI-INF/l10n/metatype.properties?rev=1836149&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-segment-tar/src/main/resources/OSGI-INF/l10n/metatype.properties (added) +++ jackrabbit/oak/trunk/oak-segment-tar/src/main/resources/OSGI-INF/l10n/metatype.properties Wed Jul 18 07:42:54 2018 @@ -0,0 +1,50 @@ +# +# 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. +# + +# suppress inspection "UnusedProperty" for whole file + +org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService.name = Apache Jackrabbit Oak Segment Tar Cold Standby Service +org.apache.jackrabbit.oak.segment.standby.store.StandbyStoreService.description = Provides continuous backups of repositories based on Segment Tar + +mode.name = Mode +mode.description = TarMK Cold Standby mode (primary or standby) + +port.name = Port +port.description = TCP/IP port to use + +primary.host.name = Primary Host +primary.host.description = Primary host (standby mode only) + +interval.name = Sync interval (seconds) +interval.description = Sync interval in seconds (standby mode only) + +primary.allowed-client-ip-ranges.name = Allowed IP-Ranges +primary.allowed-client-ip-ranges.description = Accept incoming requests for these host names and IP-ranges only (primary mode only) + +secure.name = Secure +secure.description = Use secure connections + +org.apache.sling.installer.configuration.persist.name = Persist configuration +org.apache.sling.installer.configuration.persist.description = Must be always disabled to avoid storing the configuration in the repository + +standby.readtimeout.name = Standby Read Timeout +standby.readtimeout.description = Timeout for requests issued from the standby instance in milliseconds + +standby.autoclean.name = Standby Automatic Cleanup +standby.autoclean.description = Call the cleanup method when the root segment Garbage Collector (GC) generation number increases \ No newline at end of file Modified: jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java?rev=1836149&r1=1836148&r2=1836149&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java (original) +++ jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java Wed Jul 18 07:42:54 2018 @@ -35,9 +35,9 @@ public class SegmentNodeStoreFactoryTest protected void registerSegmentNodeStoreService(boolean customBlobStore) { Map<String, Object> properties = newHashMap(); - properties.put("role", "some-role"); - properties.put("customBlobStore", customBlobStore); - properties.put("repository.home", folder.getRoot().getAbsolutePath()); + properties.put(SegmentNodeStoreFactory.ROLE, "some-role"); + properties.put(SegmentNodeStoreFactory.CUSTOM_BLOB_STORE, customBlobStore); + properties.put(SegmentNodeStoreService.REPOSITORY_HOME_DIRECTORY, folder.getRoot().getAbsolutePath()); segmentNodeStoreFactory = context.registerInjectActivateService(new SegmentNodeStoreFactory(), properties); } Modified: jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/ComponentDescriptor.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/ComponentDescriptor.java?rev=1836149&r1=1836148&r2=1836149&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/ComponentDescriptor.java (original) +++ jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/ComponentDescriptor.java Wed Jul 18 07:42:54 2018 @@ -21,7 +21,6 @@ package org.apache.jackrabbit.oak.segmen import java.io.InputStream; -import javax.print.attribute.HashPrintServiceAttributeSet; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -48,10 +47,6 @@ class ComponentDescriptor { return element.hasAttribute(name) && element.getAttribute(name).equals(value); } - private static boolean hasNoAttribute(Element element, String name) { - return !element.hasAttribute(name); - } - boolean hasName(String name) { return hasAttribute(root, "name", name); } @@ -187,8 +182,6 @@ class ComponentDescriptor { private String unbind; - private String field; - private HasReference(String name) { this.name = name; } @@ -253,11 +246,6 @@ class ComponentDescriptor { return this; } - HasReference withField(String field) { - this.field = field; - return this; - } - boolean check() { NodeList references = root.getElementsByTagName("reference"); for (int i = 0; i < references.getLength(); i++) { @@ -266,10 +254,10 @@ class ComponentDescriptor { if (iface != null && !hasAttribute(reference, "interface", iface)) { return false; } - if (cardinality != null && !hasValidCardinality(reference)) { + if (cardinality != null && !hasAttribute(reference, "cardinality", cardinality)) { return false; } - if (policy != null && !hasValidPolicy(reference)) { + if (policy != null && !hasAttribute(reference, "policy", policy)) { return false; } if (policyOption != null && !hasAttribute(reference, "policy-option", policyOption)) { @@ -284,29 +272,12 @@ class ComponentDescriptor { if (unbind != null && !hasAttribute(reference, "unbind", unbind)) { return false; } - if (field != null && !hasAttribute(reference, "field", field)) { - return false; - } return true; } } return false; } - private boolean hasValidCardinality(Element reference) { - if (cardinality.equals("1..1") && hasNoAttribute(reference, "cardinality")) { - return true; - } - return hasAttribute(reference, "cardinality", cardinality); - } - - private boolean hasValidPolicy(Element reference) { - if (policy.equals("static") && hasNoAttribute(reference, "policy")) { - return true; - } - return hasAttribute(reference, "policy", policy); - } - } HasReference hasReference(String name) { Modified: jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreFactoryTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreFactoryTest.java?rev=1836149&r1=1836148&r2=1836149&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreFactoryTest.java (original) +++ jackrabbit/oak/trunk/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/osgi/SegmentNodeStoreFactoryTest.java Wed Jul 18 07:42:54 2018 @@ -52,32 +52,36 @@ public class SegmentNodeStoreFactoryTest .withStaticPolicy() .withGreedyPolicyOption() .withTarget("(&(!(split.blobstore=old))(!(split.blobstore=new)))") - .withField("blobStore") + .withBind("bindBlobStore") + .withUnbind("unbindBlobStore") .check()); assertTrue(cd.hasReference("segmentStore") .withInterface("org.apache.jackrabbit.oak.segment.spi.persistence.SegmentNodeStorePersistence") .withOptionalUnaryCardinality() .withStaticPolicy() .withGreedyPolicyOption() - .withField("segmentStore") + .withBind("bindSegmentStore") + .withUnbind("unbindSegmentStore") .check()); assertTrue(cd.hasReference("statisticsProvider") .withInterface("org.apache.jackrabbit.oak.stats.StatisticsProvider") .withMandatoryUnaryCardinality() .withStaticPolicy() - .withField("statisticsProvider") + .withBind("bindStatisticsProvider") + .withUnbind("unbindStatisticsProvider") .check()); } @Test public void testMetatypeInformation() throws Exception { - MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory$Configuration.xml")); + MetatypeInformation mi = MetatypeInformation.open(getClass().getResourceAsStream("/OSGI-INF/metatype/org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory.xml")); assertTrue(mi.hasDesignate() + .withPid("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory") .withFactoryPid("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory") - .withReference("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory$Configuration") + .withReference("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory") .check()); - ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory$Configuration"); + ObjectClassDefinition ocd = mi.getObjectClassDefinition("org.apache.jackrabbit.oak.segment.SegmentNodeStoreFactory"); assertTrue(ocd.hasAttributeDefinition("role") .withStringType() .check());
