Index: oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java =================================================================== --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java (revision 1773081) +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactory.java (working copy) @@ -16,16 +16,13 @@ */ package org.apache.jackrabbit.oak.segment; -import static com.google.common.base.Preconditions.checkState; -import static org.apache.jackrabbit.oak.osgi.OsgiUtil.lookupConfigurationThenFramework; -import static org.apache.jackrabbit.oak.segment.file.FileStoreBuilder.fileStoreBuilder; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.asCloseable; +import static org.apache.jackrabbit.oak.segment.SegmentNodeStoreService.property; import static org.apache.jackrabbit.oak.spi.blob.osgi.SplitBlobStoreService.ONLY_STANDALONE_TARGET; -import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean; -import java.io.File; import java.io.IOException; -import java.util.Dictionary; -import java.util.Hashtable; +import java.util.HashMap; +import java.util.Map; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; @@ -35,28 +32,19 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.ReferencePolicy; -import org.apache.jackrabbit.commons.SimpleValueFactory; -import org.apache.jackrabbit.oak.api.Descriptors; -import org.apache.jackrabbit.oak.commons.PropertiesUtil; +import org.apache.felix.scr.annotations.ReferencePolicyOption; +import org.apache.jackrabbit.oak.commons.IOUtils; import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard; -import org.apache.jackrabbit.oak.plugins.identifier.ClusterRepositoryInfo; -import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder; -import org.apache.jackrabbit.oak.segment.file.FileStoreStatsMBean; -import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException; 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.state.ProxyNodeStore; -import org.apache.jackrabbit.oak.spi.whiteboard.Registration; -import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardExecutor; import org.apache.jackrabbit.oak.stats.StatisticsProvider; -import org.apache.jackrabbit.oak.util.GenericDescriptors; -import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.io.Closer; + /** * A factory allowing creation of secondary segment node stores. *

@@ -70,10 +58,8 @@ description = "Factory allowing configuration of adjacent instances of " + "NodeStore implementation based on Segment model besides a default SegmentNodeStore in same setup." ) -public class SegmentNodeStoreFactory extends ProxyNodeStore { +public class SegmentNodeStoreFactory { - public static final String NAME = "name"; - @Property( label = "Role", description="As multiple SegmentNodeStores can be configured, this parameter defines the role " + @@ -81,33 +67,6 @@ ) public static final String ROLE = "role"; - @Property( - label = "Directory", - description="Directory location used to store the segment tar files. If not specified then looks " + - "for framework property 'repository.home' otherwise use a subdirectory with name 'tarmk'" - ) - public static final String DIRECTORY = "repository.home"; - - @Property( - label = "Mode", - description="TarMK mode (64 for memory mapping, 32 for normal file access)" - ) - public static final String MODE = "tarmk.mode"; - - @Property( - intValue = 256, - label = "Maximum Tar File Size (MB)", - description = "TarMK maximum file size (MB)" - ) - public static final String SIZE = "tarmk.size"; - - @Property( - intValue = 256, - label = "Cache size (MB)", - description = "Cache size for storing most recently used Segments" - ) - public static final String CACHE = "cache"; - @Property(boolValue = false, label = "Custom BlobStore", description = "Boolean value indicating that a custom BlobStore is to be used. " + @@ -122,267 +81,59 @@ private final Logger log = LoggerFactory.getLogger(getClass()); - private String name; - - private FileStore store; - - private volatile SegmentNodeStore segmentNodeStore; - - private ComponentContext context; - - @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, - policy = ReferencePolicy.DYNAMIC, target = ONLY_STANDALONE_TARGET) + @Reference( + cardinality = ReferenceCardinality.OPTIONAL_UNARY, + policy = ReferencePolicy.STATIC, + policyOption = ReferencePolicyOption.GREEDY, + target = ONLY_STANDALONE_TARGET + ) private volatile BlobStore blobStore; @Reference private StatisticsProvider statisticsProvider = StatisticsProvider.NOOP; - private ServiceRegistration storeRegistration; - private Registration fileStoreStatsMBean; - private WhiteboardExecutor executor; + private Closer registrations = Closer.create(); - private boolean customBlobStore; - - private String role; - - private boolean registerRepositoryDescriptors; - - private ServiceRegistration clusterIdDescriptorRegistration; - - private ServiceRegistration discoveryLiteDescriptorRegistration; - - @Override - protected SegmentNodeStore getNodeStore() { - checkState(segmentNodeStore != null, "service must be activated when used"); - return segmentNodeStore; - } - @Activate public void activate(ComponentContext context) throws IOException { - this.context = context; - this.name = PropertiesUtil.toString(context.getProperties().get(NAME), "SegmentNodeStore instance"); - this.role = property(ROLE); - //In secondaryNodeStore mode customBlobStore is always enabled - this.customBlobStore = Boolean.parseBoolean(property(CUSTOM_BLOB_STORE)) || isSecondaryStoreMode(); - this.registerRepositoryDescriptors = Boolean.parseBoolean(property(REGISTER_DESCRIPTORS)); - log.info("activate: SegmentNodeStore '"+role+"' starting."); + 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 registerRepositoryDescriptors = Boolean.parseBoolean(property(REGISTER_DESCRIPTORS, context)); + log.info("activate: SegmentNodeStore '" + role + "' starting."); if (blobStore == null && customBlobStore) { log.info("BlobStore use enabled. SegmentNodeStore would be initialized when BlobStore would be available"); - } else { - registerNodeStore(); + return; } - } - protected void bindBlobStore(BlobStore blobStore) throws IOException { - this.blobStore = blobStore; - registerNodeStore(); - } + if (role != null) { + registrations = Closer.create(); + OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext()); + final SegmentNodeStore store = SegmentNodeStoreService.registerSegmentStore(context, blobStore, + statisticsProvider, registrations, whiteboard, role, registerRepositoryDescriptors); + if (store != null) { + Map props = new HashMap(); + props.put(NodeStoreProvider.ROLE, role); - protected void unbindBlobStore(BlobStore blobStore){ - this.blobStore = null; - unregisterNodeStore(); + 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); + } + } } @Deactivate public void deactivate() { - unregisterNodeStore(); - - synchronized (this) { - segmentNodeStore = null; - - if (store != null) { - store.close(); - store = null; - } + if (registrations != null) { + IOUtils.closeQuietly(registrations); + registrations = null; } } - - private synchronized void registerNodeStore() throws IOException { - if (registerSegmentStore() && role != null) { - registerNodeStoreProvider(); - } - } - - private boolean isSecondaryStoreMode() { - return "secondary".equals(role); - } - - private void registerNodeStoreProvider() { - SegmentNodeStore.SegmentNodeStoreBuilder nodeStoreBuilder = SegmentNodeStoreBuilders.builder(store); - segmentNodeStore = nodeStoreBuilder.build(); - Dictionary props = new Hashtable(); - props.put(NodeStoreProvider.ROLE, role); - storeRegistration = context.getBundleContext().registerService(NodeStoreProvider.class.getName(), new NodeStoreProvider() { - @Override - public NodeStore getNodeStore() { - return SegmentNodeStoreFactory.this; - } - }, - props); - log.info("Registered NodeStoreProvider backed by SegmentNodeStore of type '{}'", role); - - if (registerRepositoryDescriptors) { - - log.info("Registering JCR descriptors"); - - // TODO - copied from SegmentNodeStoreService - // 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( - ClusterRepositoryInfo.getOrCreateId(segmentNodeStore)), true, false); - clusterIdDescriptorRegistration = context.getBundleContext().registerService( - Descriptors.class.getName(), - clusterIdDesc, - new Hashtable<>() - ); - - // Register "discovery lite" descriptors - discoveryLiteDescriptorRegistration = context.getBundleContext().registerService( - Descriptors.class.getName(), - new SegmentDiscoveryLiteDescriptors(segmentNodeStore), - new Hashtable<>() - ); - } - - } - - private boolean registerSegmentStore() throws IOException { - if (context == null) { - log.info("Component still not activated. Ignoring the initialization call"); - return false; - } - - OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext()); - - // Build the FileStore - - FileStoreBuilder builder = fileStoreBuilder(getDirectory()) - .withSegmentCacheSize(getCacheSize()) - .withMaxFileSize(getMaxFileSize()) - .withMemoryMapping(getMode().equals("64")) - .withStatisticsProvider(statisticsProvider); - - if (customBlobStore) { - log.info("Initializing SegmentNodeStore with BlobStore [{}]", blobStore); - builder.withBlobStore(blobStore); - } - - try { - store = builder.build(); - } catch (InvalidFileStoreVersionException e) { - log.error("The segment store data is not compatible with the current version. Please use oak-segment-tar or a different version of oak-segment."); - return false; - } - - // Listen for Executor services on the whiteboard - - executor = new WhiteboardExecutor(); - executor.start(whiteboard); - - // Expose statistics about the FileStore - - fileStoreStatsMBean = registerMBean( - whiteboard, - FileStoreStatsMBean.class, - store.getStats(), - FileStoreStatsMBean.TYPE, - "FileStore '" + role + "' statistics" - ); - - return true; - } - - private void unregisterNodeStore() { - if (storeRegistration != null) { - storeRegistration.unregister(); - storeRegistration = null; - } - if (fileStoreStatsMBean != null) { - fileStoreStatsMBean.unregister(); - fileStoreStatsMBean = null; - } - if (executor != null) { - executor.stop(); - executor = null; - } - if (clusterIdDescriptorRegistration != null) { - clusterIdDescriptorRegistration.unregister(); - clusterIdDescriptorRegistration = null; - } - if (discoveryLiteDescriptorRegistration != null) { - discoveryLiteDescriptorRegistration.unregister(); - discoveryLiteDescriptorRegistration = null; - } - } - - private File getBaseDirectory() { - String directory = property(DIRECTORY); - - if (directory != null) { - return new File(directory); - } - - return new File("tarmk"); - } - - private File getDirectory() { - String dirName = "segmentstore"; - if (role != null){ - dirName = role + "-" + dirName; - } - return new File(getBaseDirectory(), dirName); - } - - private String getMode() { - String mode = property(MODE); - - if (mode != null) { - return mode; - } - - return System.getProperty(MODE, System.getProperty("sun.arch.data.model", "32")); - } - - private String getCacheSizeProperty() { - String cache = property(CACHE); - - if (cache != null) { - return cache; - } - - return System.getProperty(CACHE); - } - - private int getCacheSize() { - return Integer.parseInt(getCacheSizeProperty()); - } - - private String getMaxFileSizeProperty() { - String size = property(SIZE); - - if (size != null) { - return size; - } - - return System.getProperty(SIZE, "256"); - } - - private int getMaxFileSize() { - return Integer.parseInt(getMaxFileSizeProperty()); - } - - private String property(String name) { - return lookupConfigurationThenFramework(context, name); - } - - //------------------------------------------------------------< Object >-- - - @Override - public String toString() { - return name + ": " + segmentNodeStore + "[role:" + role + "]"; - } - } Index: oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java =================================================================== --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java (revision 1773081) +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java (working copy) @@ -23,7 +23,13 @@ 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.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.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; 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; @@ -32,22 +38,24 @@ 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.segment.file.FileStoreBuilder.fileStoreBuilder; import static org.apache.jackrabbit.oak.spi.blob.osgi.SplitBlobStoreService.ONLY_STANDALONE_TARGET; import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean; import java.io.ByteArrayInputStream; +import java.io.Closeable; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; -import java.util.Dictionary; -import java.util.Hashtable; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; -import com.google.common.base.Strings; -import com.google.common.base.Supplier; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.ConfigurationPolicy; @@ -64,6 +72,7 @@ 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.commons.IOUtils; import org.apache.jackrabbit.oak.osgi.ObserverTracker; import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard; import org.apache.jackrabbit.oak.plugins.blob.BlobGC; @@ -91,6 +100,7 @@ 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.CompositeRegistration; import org.apache.jackrabbit.oak.spi.whiteboard.Registration; import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardExecutor; @@ -98,11 +108,15 @@ import org.apache.jackrabbit.oak.stats.StatisticsProvider; import org.apache.jackrabbit.oak.util.GenericDescriptors; import org.osgi.framework.Constants; -import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Strings; +import com.google.common.base.Supplier; +import com.google.common.collect.Lists; +import com.google.common.io.Closer; + /** * An OSGi wrapper for the segment node store. */ @@ -117,6 +131,8 @@ ) public class SegmentNodeStoreService { + private static final Logger log = LoggerFactory.getLogger(SegmentNodeStoreService.class); + @Property( label = "Directory", description="Directory location used to store the segment tar files. If not specified then looks " + @@ -131,7 +147,7 @@ public static final String MODE = "tarmk.mode"; @Property( - intValue = 256, + intValue = DEFAULT_MAX_FILE_SIZE, label = "Maximum Tar File Size (MB)", description = "TarMK maximum file size (MB)" ) @@ -138,7 +154,7 @@ public static final String SIZE = "tarmk.size"; @Property( - intValue = 256, + intValue = DEFAULT_SEGMENT_CACHE_MB, label = "Segment cache size (MB)", description = "Cache size for storing most recently used segments" ) @@ -145,7 +161,7 @@ public static final String SEGMENT_CACHE_SIZE = "segmentCache.size"; @Property( - intValue = 256, + intValue = DEFAULT_STRING_CACHE_MB, label = "String cache size (MB)", description = "Cache size for storing most recently used strings" ) @@ -152,7 +168,7 @@ public static final String STRING_CACHE_SIZE = "stringCache.size"; @Property( - intValue = 64, + intValue = DEFAULT_TEMPLATE_CACHE_MB, label = "Template cache size (MB)", description = "Cache size for storing most recently used templates" ) @@ -159,7 +175,7 @@ public static final String TEMPLATE_CACHE_SIZE = "templateCache.size"; @Property( - intValue = 15000, + intValue = DEFAULT_STRING_CACHE_SIZE_OSGi, label = "String deduplication cache size (#items)", description = "Maximum number of strings to keep in the deduplication cache" ) @@ -166,7 +182,7 @@ public static final String STRING_DEDUPLICATION_CACHE_SIZE = "stringDeduplicationCache.size"; @Property( - intValue = 3000, + intValue = DEFAULT_TEMPLATE_CACHE_SIZE_OSGi, label = "Template deduplication cache size (#items)", description = "Maximum number of templates to keep in the deduplication cache" ) @@ -173,7 +189,7 @@ public static final String TEMPLATE_DEDUPLICATION_CACHE_SIZE = "templateDeduplicationCache.size"; @Property( - intValue = 1048576, + 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." @@ -263,16 +279,6 @@ ) public static final String BACKUP_DIRECTORY = "repository.backup.dir"; - private final Logger log = LoggerFactory.getLogger(getClass()); - - private FileStore store; - - private ObserverTracker observerTracker; - - private GCMonitorTracker gcMonitor; - - private ComponentContext context; - @Reference( cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.STATIC, @@ -284,13 +290,8 @@ @Reference private StatisticsProvider statisticsProvider = StatisticsProvider.NOOP; - private ServiceRegistration storeRegistration; - private ServiceRegistration providerRegistration; + private Closer registrations = Closer.create(); - private final List registrations = new ArrayList<>(); - private WhiteboardExecutor executor; - private boolean customBlobStore; - /** * Blob modified before this time duration would be considered for Blob GC */ @@ -319,127 +320,155 @@ @Activate public void activate(ComponentContext context) throws IOException { - this.context = context; - this.customBlobStore = Boolean.parseBoolean(property(CUSTOM_BLOB_STORE)); - - if (blobStore == null && customBlobStore) { + if (blobStore == null && hasCustomBlobStore(context)) { log.info("BlobStore use enabled. SegmentNodeStore would be initialized when BlobStore would be available"); return; } - + registrations = Closer.create(); OsgiWhiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext()); + registerSegmentStore(context, blobStore, statisticsProvider, registrations, whiteboard, null, true); + } + /** + * 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 + * @param blobStore + * @param statisticsProvider + * @param registrations + * @param whiteboard + * @param role + * @param registerRepositoryDescriptors + * @return a configured segment node store, or {@code null} if the setup + * failed + * @throws IOException + */ + static SegmentNodeStore registerSegmentStore(@Nonnull ComponentContext context, @Nullable BlobStore blobStore, + @Nonnull StatisticsProvider statisticsProvider, @Nonnull Closer registrations, + @Nonnull OsgiWhiteboard whiteboard, @Nullable String role, boolean registerRepositoryDescriptors) + throws IOException { + // Listen for GCMonitor services - gcMonitor = new GCMonitorTracker(); + GCMonitorTracker gcMonitor = new GCMonitorTracker(); gcMonitor.start(whiteboard); // Create the gc options - SegmentGCOptions gcOptions = newGCOptions(); + SegmentGCOptions gcOptions = newGCOptions(context); // Build the FileStore - FileStoreBuilder builder = fileStoreBuilder(getDirectory()) - .withSegmentCacheSize(getSegmentCacheSize()) - .withStringCacheSize(getStringCacheSize()) - .withTemplateCacheSize(getTemplateCacheSize()) - .withStringDeduplicationCacheSize(getStringDeduplicationCacheSize()) - .withTemplateDeduplicationCacheSize(getTemplateDeduplicationCacheSize()) - .withNodeDeduplicationCacheSize(getNodeDeduplicationCacheSize()) - .withMaxFileSize(getMaxFileSize()) - .withMemoryMapping(getMode().equals("64")) + FileStoreBuilder builder = fileStoreBuilder(getDirectory(context, role)) + .withSegmentCacheSize(getSegmentCacheSize(context)) + .withStringCacheSize(getStringCacheSize(context)) + .withTemplateCacheSize(getTemplateCacheSize(context)) + .withStringDeduplicationCacheSize(getStringDeduplicationCacheSize(context)) + .withTemplateDeduplicationCacheSize(getTemplateDeduplicationCacheSize(context)) + .withNodeDeduplicationCacheSize(getNodeDeduplicationCacheSize(context)) + .withMaxFileSize(getMaxFileSize(context)) + .withMemoryMapping(getMode(context).equals("64")) .withGCMonitor(gcMonitor) .withStatisticsProvider(statisticsProvider) .withGCOptions(gcOptions); - if (customBlobStore) { + if (hasCustomBlobStore(context)) { log.info("Initializing SegmentNodeStore with BlobStore [{}]", blobStore); builder.withBlobStore(blobStore); } - if (toBoolean(property(STANDBY), true)) { + if (!isStandbyInstance(context)) { builder.withSnfeListener(IGNORE_SNFE); } + final FileStore store; try { store = builder.build(); } catch (InvalidFileStoreVersionException e) { log.error("The segment store data is not compatible with the current version. Please use oak-segment or a different version of oak-segment-tar."); - return; + return null; } + // store should be closed last + registrations.register(store); + registrations.register(asCloseable(gcMonitor)); + // Listen for Executor services on the whiteboard + + WhiteboardExecutor executor = new WhiteboardExecutor(); + executor.start(whiteboard); + registrations.register(asCloseable(executor)); + + List mbeans = Lists.newArrayList(); + // Expose stats about the segment cache CacheStatsMBean segmentCacheStats = store.getSegmentCacheStats(); - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, CacheStatsMBean.class, segmentCacheStats, CacheStats.TYPE, - segmentCacheStats.getName() + appendRole(segmentCacheStats.getName(), role) )); // Expose stats about the string and template caches CacheStatsMBean stringCacheStats = store.getStringCacheStats(); - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, CacheStatsMBean.class, stringCacheStats, CacheStats.TYPE, - stringCacheStats.getName() + appendRole(stringCacheStats.getName(), role) )); CacheStatsMBean templateCacheStats = store.getTemplateCacheStats(); - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, CacheStatsMBean.class, templateCacheStats, CacheStats.TYPE, - templateCacheStats.getName() + appendRole(templateCacheStats.getName(), role) )); CacheStatsMBean stringDeduplicationCacheStats = store.getStringDeduplicationCacheStats(); if (stringDeduplicationCacheStats != null) { - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, CacheStatsMBean.class, stringDeduplicationCacheStats, CacheStats.TYPE, - stringDeduplicationCacheStats.getName())); + appendRole(stringDeduplicationCacheStats.getName(), role))); } CacheStatsMBean templateDeduplicationCacheStats = store.getTemplateDeduplicationCacheStats(); if (templateDeduplicationCacheStats != null) { - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, CacheStatsMBean.class, templateDeduplicationCacheStats, CacheStats.TYPE, - templateDeduplicationCacheStats.getName())); + appendRole(templateDeduplicationCacheStats.getName(), role))); } CacheStatsMBean nodeDeduplicationCacheStats = store.getNodeDeduplicationCacheStats(); if (nodeDeduplicationCacheStats != null) { - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, CacheStatsMBean.class, nodeDeduplicationCacheStats, CacheStats.TYPE, - nodeDeduplicationCacheStats.getName())); + appendRole(nodeDeduplicationCacheStats.getName(), role))); } - // Listen for Executor services on the whiteboard - - executor = new WhiteboardExecutor(); - executor.start(whiteboard); - // Expose an MBean to managing and monitoring garbage collection final FileStoreGCMonitor fsgcm = new FileStoreGCMonitor(Clock.SIMPLE); - registrations.add(new CompositeRegistration( + mbeans.add(new CompositeRegistration( whiteboard.register(GCMonitor.class, fsgcm, emptyMap()), registerMBean( - whiteboard, - SegmentRevisionGC.class, - new SegmentRevisionGCMBean(store, gcOptions, fsgcm), - SegmentRevisionGC.TYPE, - "Segment node store revision garbage collection" - ))); + whiteboard, + SegmentRevisionGC.class, + new SegmentRevisionGCMBean(store, gcOptions, fsgcm), + SegmentRevisionGC.TYPE, + appendRole("Segment node store revision garbage collection", role) + ))); Runnable cancelGC = new Runnable() { @@ -455,60 +484,64 @@ return fsgcm.getStatus(); } }; - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, RevisionGCMBean.class, new RevisionGC(store.getGCRunner(), cancelGC, statusMessage, executor), RevisionGCMBean.TYPE, - "Revision garbage collection" + appendRole("Revision garbage collection", role) )); // Expose statistics about the FileStore - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, FileStoreStatsMBean.class, store.getStats(), FileStoreStatsMBean.TYPE, - "FileStore statistics" + appendRole("FileStore statistics", role) )); // register segment node store - final long blobGcMaxAgeInSecs = toLong(property(PROP_BLOB_GC_MAX_AGE), DEFAULT_BLOB_GC_MAX_AGE); - SegmentNodeStore.SegmentNodeStoreBuilder segmentNodeStoreBuilder = SegmentNodeStoreBuilders.builder(store) .withStatisticsProvider(statisticsProvider); - if (toBoolean(property(STANDBY), false)) { + if (isStandbyInstance(context)) { segmentNodeStoreBuilder.dispatchChanges(false); } SegmentNodeStore segmentNodeStore = segmentNodeStoreBuilder.build(); - observerTracker = new ObserverTracker(segmentNodeStore); + ObserverTracker observerTracker = new ObserverTracker(segmentNodeStore); observerTracker.start(context.getBundleContext()); + registrations.register(asCloseable(observerTracker)); - registrations.add(registerMBean(whiteboard, CheckpointMBean.class, new SegmentCheckpointMBean(segmentNodeStore), - CheckpointMBean.TYPE, "Segment node store checkpoint management")); + mbeans.add(registerMBean( + whiteboard, + CheckpointMBean.class, + new SegmentCheckpointMBean(segmentNodeStore), CheckpointMBean.TYPE, + appendRole("Segment node store checkpoint management", role))); - // 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( - ClusterRepositoryInfo.getOrCreateId(segmentNodeStore)), true, false); - registrations.add(whiteboard.register( - Descriptors.class, - clusterIdDesc, - Collections.emptyMap() - )); + if (registerRepositoryDescriptors) { + // 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( + ClusterRepositoryInfo.getOrCreateId(segmentNodeStore)), true, false); + mbeans.add(whiteboard.register( + Descriptors.class, + clusterIdDesc, + Collections.emptyMap() + )); - // Register "discovery lite" descriptors - registrations.add(whiteboard.register( - Descriptors.class, - new SegmentDiscoveryLiteDescriptors(segmentNodeStore), - Collections.emptyMap() - )); + // Register "discovery lite" descriptors + mbeans.add(whiteboard.register( + Descriptors.class, + new SegmentDiscoveryLiteDescriptors(segmentNodeStore), + Collections.emptyMap() + )); + } // If a shared data store register the repo id in the data store String repoId = ""; @@ -522,9 +555,9 @@ } if (blobStore instanceof BlobTrackingStore) { - final long trackSnapshotInterval = toLong(property(PROP_BLOB_SNAPSHOT_INTERVAL), + final long trackSnapshotInterval = toLong(property(PROP_BLOB_SNAPSHOT_INTERVAL, context), DEFAULT_BLOB_SNAPSHOT_INTERVAL); - String root = property(DIRECTORY); + String root = property(DIRECTORY, context); if (Strings.isNullOrEmpty(root)) { root = "repository"; } @@ -540,6 +573,7 @@ } if (store.getBlobStore() instanceof GarbageCollectableBlobStore) { + final long blobGcMaxAgeInSecs = toLong(property(PROP_BLOB_GC_MAX_AGE, context), DEFAULT_BLOB_GC_MAX_AGE); BlobGarbageCollector gc = new MarkSweepGarbageCollector( new SegmentBlobReferenceRetriever(store), (GarbageCollectableBlobStore) store.getBlobStore(), @@ -548,32 +582,34 @@ repoId ); - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, BlobGCMBean.class, new BlobGC(gc, executor), BlobGCMBean.TYPE, - "Segment node store blob garbage collection" + appendRole("Segment node store blob garbage collection", role) )); } // Expose an MBean for backup/restore operations - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, FileStoreBackupRestoreMBean.class, - new FileStoreBackupRestoreImpl(segmentNodeStore, store.getRevisions(), store.getReader(), getBackupDirectory(), executor), - FileStoreBackupRestoreMBean.TYPE, "Segment node store backup/restore" + new FileStoreBackupRestoreImpl(segmentNodeStore, store.getRevisions(), store.getReader(), + getBackupDirectory(context, role), executor), + FileStoreBackupRestoreMBean.TYPE, + appendRole("Segment node store backup/restore", role) )); // Expose statistics about the SegmentNodeStore - registrations.add(registerMBean( + mbeans.add(registerMBean( whiteboard, SegmentNodeStoreStatsMBean.class, segmentNodeStore.getStats(), SegmentNodeStoreStatsMBean.TYPE, - "SegmentNodeStore statistics" + appendRole("SegmentNodeStore statistics", role) )); log.info("SegmentNodeStore initialized"); @@ -580,67 +616,46 @@ // Register a factory service to expose the FileStore - providerRegistration = context.getBundleContext().registerService( - SegmentStoreProvider.class.getName(), + registrations.register(asCloseable(whiteboard.register( + SegmentStoreProvider.class, new DefaultSegmentStoreProvider(store), - null - ); + Collections.emptyMap() + ))); - if (toBoolean(property(STANDBY), false)) { - return; + if (!isStandbyInstance(context)) { + Map props = new HashMap(); + props.put(Constants.SERVICE_PID, SegmentNodeStore.class.getName()); + props.put("oak.nodestore.description", new String[] { "nodeStoreType=segment" }); + registrations.register(asCloseable(whiteboard.register(NodeStore.class, segmentNodeStore, props))); } - Dictionary props = new Hashtable(); - props.put(Constants.SERVICE_PID, SegmentNodeStore.class.getName()); - props.put("oak.nodestore.description", new String[] {"nodeStoreType=segment"}); - storeRegistration = context.getBundleContext().registerService(NodeStore.class.getName(), segmentNodeStore, props); + registrations.register(asCloseable(new CompositeRegistration(mbeans))); + return segmentNodeStore; } @Deactivate public void deactivate() { - new CompositeRegistration(registrations).unregister(); - registrations.clear(); - if (providerRegistration != null) { - providerRegistration.unregister(); - providerRegistration = null; + if (registrations != null) { + IOUtils.closeQuietly(registrations); + registrations = null; } - if (storeRegistration != null) { - storeRegistration.unregister(); - storeRegistration = null; - } - if (executor != null) { - executor.stop(); - executor = null; - } - if (observerTracker != null) { - observerTracker.stop(); - observerTracker = null; - } - if (gcMonitor != null) { - gcMonitor.stop(); - gcMonitor = null; - } - if (store != null) { - store.close(); - store = null; - } } - private SegmentGCOptions newGCOptions() { - boolean pauseCompaction = toBoolean(property(PAUSE_COMPACTION), PAUSE_DEFAULT); - int retryCount = toInteger(property(COMPACTION_RETRY_COUNT), RETRY_COUNT_DEFAULT); - int forceTimeout = toInteger(property(COMPACTION_FORCE_TIMEOUT), FORCE_TIMEOUT_DEFAULT); - int retainedGenerations = toInteger(property(RETAINED_GENERATIONS), RETAINED_GENERATIONS_DEFAULT); + private static SegmentGCOptions newGCOptions(ComponentContext context) { + boolean pauseCompaction = toBoolean(property(PAUSE_COMPACTION, context), PAUSE_DEFAULT); + int retryCount = toInteger(property(COMPACTION_RETRY_COUNT, context), RETRY_COUNT_DEFAULT); + int forceTimeout = toInteger(property(COMPACTION_FORCE_TIMEOUT, context), FORCE_TIMEOUT_DEFAULT); + int retainedGenerations = toInteger(property(RETAINED_GENERATIONS, context), RETAINED_GENERATIONS_DEFAULT); - long sizeDeltaEstimation = toLong(property(COMPACTION_SIZE_DELTA_ESTIMATION), SIZE_DELTA_ESTIMATION_DEFAULT); - int memoryThreshold = toInteger(property(MEMORY_THRESHOLD), MEMORY_THRESHOLD_DEFAULT); - boolean disableEstimation = toBoolean(property(COMPACTION_DISABLE_ESTIMATION), DISABLE_ESTIMATION_DEFAULT); + long sizeDeltaEstimation = toLong(property(COMPACTION_SIZE_DELTA_ESTIMATION, context), SIZE_DELTA_ESTIMATION_DEFAULT); + int memoryThreshold = toInteger(property(MEMORY_THRESHOLD, context), MEMORY_THRESHOLD_DEFAULT); + boolean disableEstimation = toBoolean(property(COMPACTION_DISABLE_ESTIMATION, context), DISABLE_ESTIMATION_DEFAULT); - if (property("compaction.gainThreshold") != null) { + if (property("compaction.gainThreshold", context) != null) { log.warn("Deprecated property compaction.gainThreshold was detected. In order to configure compaction please use the new property " + "compaction.sizeDeltaEstimation. For turning off estimation, the new property compaction.disableEstimation should be used."); } - long gcProgressLog = toLong(property(GC_PROGRESS_LOG), GC_PROGRESS_LOG_DEFAULT); + long gcProgressLog = toLong(property(GC_PROGRESS_LOG, context), GC_PROGRESS_LOG_DEFAULT); return new SegmentGCOptions(pauseCompaction, retryCount, forceTimeout) .setRetainedGenerations(retainedGenerations) @@ -650,9 +665,17 @@ .withGCNodeWriteMonitor(gcProgressLog); } - private File getBaseDirectory() { - String directory = property(DIRECTORY); + private static boolean isStandbyInstance(ComponentContext context) { + return Boolean.parseBoolean(property(STANDBY, context)); + } + private static boolean hasCustomBlobStore(ComponentContext context) { + return Boolean.parseBoolean(property(CUSTOM_BLOB_STORE, context)); + } + + private static File getBaseDirectory(ComponentContext context) { + String directory = property(DIRECTORY, context); + if (directory != null) { return new File(directory); } @@ -660,22 +683,20 @@ return new File("tarmk"); } - private File getDirectory() { - return new File(getBaseDirectory(), "segmentstore"); + private static File getDirectory(ComponentContext context, String role) { + return new File(getBaseDirectory(context), appendRole("segmentstore", role)); } - - private File getBackupDirectory() { - String backupDirectory = property(BACKUP_DIRECTORY); - + + private static File getBackupDirectory(ComponentContext context, String role) { + String backupDirectory = property(BACKUP_DIRECTORY, context); if (backupDirectory != null) { return new File(backupDirectory); } - - return new File(getBaseDirectory(), "segmentstore-backup"); + return new File(getBaseDirectory(context), appendRole("segmentstore-backup", role)); } - private String getMode() { - String mode = property(MODE); + private static String getMode(ComponentContext context) { + String mode = property(MODE, context); if (mode != null) { return mode; @@ -684,8 +705,8 @@ return System.getProperty(MODE, System.getProperty("sun.arch.data.model", "32")); } - private String getCacheSize(String propertyName) { - String cacheSize = property(propertyName); + private static String getCacheSize(String propertyName, ComponentContext context) { + String cacheSize = property(propertyName, context); if (cacheSize != null) { return cacheSize; @@ -694,48 +715,75 @@ return System.getProperty(propertyName); } - private int getSegmentCacheSize() { - return Integer.parseInt(getCacheSize(SEGMENT_CACHE_SIZE)); + private static int getSegmentCacheSize(ComponentContext context) { + return toInteger(getCacheSize(SEGMENT_CACHE_SIZE, context), DEFAULT_SEGMENT_CACHE_MB); } - private int getStringCacheSize() { - return Integer.parseInt(getCacheSize(STRING_CACHE_SIZE)); + private static int getStringCacheSize(ComponentContext context) { + return toInteger(getCacheSize(STRING_CACHE_SIZE, context), DEFAULT_STRING_CACHE_MB); } - private int getTemplateCacheSize() { - return Integer.parseInt(getCacheSize(TEMPLATE_CACHE_SIZE)); + private static int getTemplateCacheSize(ComponentContext context) { + return toInteger(getCacheSize(TEMPLATE_CACHE_SIZE, context), DEFAULT_TEMPLATE_CACHE_MB); } - private int getStringDeduplicationCacheSize() { - return Integer.parseInt(getCacheSize(STRING_DEDUPLICATION_CACHE_SIZE)); + private static int getStringDeduplicationCacheSize(ComponentContext context) { + return toInteger(getCacheSize(STRING_DEDUPLICATION_CACHE_SIZE, context), DEFAULT_STRING_CACHE_SIZE_OSGi); } - private int getTemplateDeduplicationCacheSize() { - return Integer.parseInt(getCacheSize(TEMPLATE_DEDUPLICATION_CACHE_SIZE)); + private static int getTemplateDeduplicationCacheSize(ComponentContext context) { + return toInteger(getCacheSize(TEMPLATE_DEDUPLICATION_CACHE_SIZE, context), DEFAULT_TEMPLATE_CACHE_SIZE_OSGi); } - private int getNodeDeduplicationCacheSize() { + private static int getNodeDeduplicationCacheSize(ComponentContext context) { // Round to the next power of 2 - int size = Math.max(1, Integer.parseInt(getCacheSize(NODE_DEDUPLICATION_CACHE_SIZE))); + int size = Math.max(1, + toInteger(getCacheSize(NODE_DEDUPLICATION_CACHE_SIZE, context), DEFAULT_NODE_CACHE_SIZE_OSGi)); return 1 << (32 - Integer.numberOfLeadingZeros(size - 1)); } - private String getMaxFileSizeProperty() { - String size = property(SIZE); + private static int getMaxFileSize(ComponentContext context) { + return toInteger(property(SIZE, context), DEFAULT_MAX_FILE_SIZE); + } - if (size != null) { - return size; + static String property(String name, ComponentContext context) { + return lookupConfigurationThenFramework(context, name); + } + + private static String appendRole(@Nonnull String name, @Nullable String role) { + if (role == null) { + return name; + } else { + return name + "-" + role; } + } - return System.getProperty(SIZE, "256"); + static Closeable asCloseable(final Registration r) { + return new Closeable() { + + @Override + public void close() { + r.unregister(); + } + }; } - private int getMaxFileSize() { - return Integer.parseInt(getMaxFileSizeProperty()); + private static Closeable asCloseable(final AbstractServiceTracker t) { + return new Closeable() { + @Override + public void close() { + t.stop(); + } + }; } - private String property(String name) { - return lookupConfigurationThenFramework(context, name); + private static Closeable asCloseable(final ObserverTracker t) { + return new Closeable() { + + @Override + public void close() { + t.stop(); + } + }; } - } Index: oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/WriterCacheManager.java =================================================================== --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/WriterCacheManager.java (revision 1773081) +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/WriterCacheManager.java (working copy) @@ -50,25 +50,40 @@ public abstract class WriterCacheManager { /** + * Default size of the string cache, used as default for OSGi config. + */ + public static final int DEFAULT_STRING_CACHE_SIZE_OSGi = 15000; + + /** * Default size of the string cache. * @see #getStringCache(int) */ public static final int DEFAULT_STRING_CACHE_SIZE = getInteger( - "oak.tar.stringsCacheSize", 15000); + "oak.tar.stringsCacheSize", DEFAULT_STRING_CACHE_SIZE_OSGi); /** + * Default size of the template cache, used as default for OSGi config. + */ + public static final int DEFAULT_TEMPLATE_CACHE_SIZE_OSGi = 3000; + + /** * Default size of the template cache. * @see #getTemplateCache(int) */ public static final int DEFAULT_TEMPLATE_CACHE_SIZE = getInteger( - "oak.tar.templatesCacheSize", 3000); + "oak.tar.templatesCacheSize", DEFAULT_TEMPLATE_CACHE_SIZE_OSGi); /** + * Default size of the node deduplication cache, used as default for OSGi config. + */ + public static final int DEFAULT_NODE_CACHE_SIZE_OSGi = 1048576; + + /** * Default size of the node deduplication cache. * @see #getNodeCache(int) */ public static final int DEFAULT_NODE_CACHE_SIZE = getInteger( - "oak.tar.nodeCacheSize", 1048576); + "oak.tar.nodeCacheSize", DEFAULT_NODE_CACHE_SIZE_OSGi); /** * @param generation Index: oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreBuilder.java =================================================================== --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreBuilder.java (revision 1773081) +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStoreBuilder.java (working copy) @@ -63,6 +63,8 @@ private static final boolean MEMORY_MAPPING_DEFAULT = "64".equals(System.getProperty("sun.arch.data.model", "32")); + public static final int DEFAULT_MAX_FILE_SIZE = 256; + @Nonnull private final File directory; @@ -69,7 +71,7 @@ @CheckForNull private BlobStore blobStore; // null -> store blobs inline - private int maxFileSize = 256; + private int maxFileSize = DEFAULT_MAX_FILE_SIZE; private int segmentCacheSize = DEFAULT_SEGMENT_CACHE_MB; Index: oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java =================================================================== --- oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java (revision 1773081) +++ oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreFactoryTest.java (working copy) @@ -44,7 +44,7 @@ properties.put(SegmentNodeStoreFactory.ROLE, "some-role"); properties.put(SegmentNodeStoreFactory.CUSTOM_BLOB_STORE, customBlobStore); - properties.put(SegmentNodeStoreFactory.DIRECTORY, folder.getRoot().getAbsolutePath()); + properties.put(SegmentNodeStoreService.DIRECTORY, folder.getRoot().getAbsolutePath()); segmentNodeStoreFactory = context.registerInjectActivateService(new SegmentNodeStoreFactory(), properties); } @@ -63,4 +63,5 @@ protected void assertServiceNotActivated() { assertNull(context.getService(NodeStoreProvider.class)); } + } \ No newline at end of file