Index: oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/blob/DocumentBlobGCRegistrationTest.java =================================================================== --- oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/blob/DocumentBlobGCRegistrationTest.java (revision 1816118) +++ oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/blob/DocumentBlobGCRegistrationTest.java (working copy) @@ -55,7 +55,10 @@ properties.put("repository.home", repoHome); properties.put("mongouri", MongoUtils.URL); properties.put("db", MongoUtils.DB); - service = context.registerInjectActivateService(new DocumentNodeStoreService(), properties); + MockOsgi.setConfigForPid(context.bundleContext(), + DocumentNodeStoreService.class.getName(), properties); + context.registerInjectActivateService(new DocumentNodeStoreService.Preset()); + service = context.registerInjectActivateService(new DocumentNodeStoreService()); } @Override Index: oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/blob/DocumentBlobTrackerRegistrationTest.java =================================================================== --- oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/blob/DocumentBlobTrackerRegistrationTest.java (revision 1816118) +++ oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/blob/DocumentBlobTrackerRegistrationTest.java (working copy) @@ -23,7 +23,9 @@ import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService; import org.apache.jackrabbit.oak.plugins.document.MongoUtils; +import org.apache.sling.testing.mock.osgi.MockOsgi; import org.junit.After; +import org.junit.Before; import org.junit.BeforeClass; import static com.google.common.collect.Maps.newHashMap; @@ -42,6 +44,11 @@ assumeTrue(MongoUtils.isAvailable()); } + @Before + public void registerPreset() { + context.registerInjectActivateService(new DocumentNodeStoreService.Preset()); + } + @After @Override public void tearDown() throws Exception { @@ -57,7 +64,9 @@ properties.put("repository.home", repoHome); properties.put("mongouri", MongoUtils.URL); properties.put("db", MongoUtils.DB); - service = context.registerInjectActivateService(new DocumentNodeStoreService(), properties); + MockOsgi.setConfigForPid(context.bundleContext(), + DocumentNodeStoreService.class.getName(), properties); + service = context.registerInjectActivateService(new DocumentNodeStoreService()); } @Override Index: oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentCachingDataStoreStatsTest.java =================================================================== --- oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentCachingDataStoreStatsTest.java (revision 1816118) +++ oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentCachingDataStoreStatsTest.java (working copy) @@ -28,6 +28,7 @@ import org.apache.jackrabbit.oak.spi.blob.BlobStore; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.apache.jackrabbit.oak.stats.StatisticsProvider; +import org.apache.sling.testing.mock.osgi.MockOsgi; import org.apache.sling.testing.mock.osgi.ReferenceViolationException; import org.apache.sling.testing.mock.osgi.junit.OsgiContext; import org.junit.After; @@ -128,6 +129,9 @@ properties.put("db", MongoUtils.DB); properties.put("repository.home", repoHome); properties.put(DocumentNodeStoreService.CUSTOM_BLOB_STORE, customBlobStore); + MockOsgi.setConfigForPid(context.bundleContext(), + DocumentNodeStoreService.class.getName(), properties); + context.registerInjectActivateService(new DocumentNodeStoreService.Preset()); documentNodeStoreService = context.registerInjectActivateService(new DocumentNodeStoreService(), properties); } Index: oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Configuration.java =================================================================== --- oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Configuration.java (revision 1816118) +++ oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Configuration.java (working copy) @@ -23,6 +23,7 @@ import org.osgi.service.metatype.annotations.ObjectClassDefinition; import org.osgi.service.metatype.annotations.Option; +import static org.apache.jackrabbit.oak.plugins.document.Configuration.PID; import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_CACHE_SEGMENT_COUNT; import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_CACHE_STACK_MOVE_DISTANCE; import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_CHILDREN_CACHE_PERCENTAGE; @@ -29,8 +30,10 @@ import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_DIFF_CACHE_PERCENTAGE; import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_NODE_CACHE_PERCENTAGE; import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_PREV_DOC_CACHE_PERCENTAGE; +import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_UPDATE_LIMIT; @ObjectClassDefinition( + pid = {PID}, name = "Apache Jackrabbit Oak Document NodeStore Service", description = "NodeStore implementation based on Document model. For configuration option refer " + "to http://jackrabbit.apache.org/oak/docs/osgi_config.html#DocumentNodeStore. Note that for system " + @@ -38,6 +41,11 @@ "should be done via file system based config file and this view should ONLY be used to determine which " + "options are supported") @interface Configuration { + + String PID = "org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService"; + + String PRESET_PID = PID + "Preset"; + @AttributeDefinition( name = "Mongo URI", description = "Mongo connection URI used to connect to Mongo. Refer to " + @@ -121,7 +129,7 @@ name = "Custom BlobStore", description = "Boolean value indicating that a custom BlobStore is to be used. " + "By default, for MongoDB, MongoBlobStore is used; for RDB, RDBBlobStore is used.") - boolean customBlobStore() default false; + boolean customBlobStore() default DocumentNodeStoreService.DEFAULT_CUSTOM_BLOB_STORE; @AttributeDefinition( @@ -141,7 +149,7 @@ name = "Pre-fetch external changes", description = "Boolean value indicating if external changes should " + "be pre-fetched in a background thread.") - boolean prefetchExternalChanges() default false; + boolean prefetchExternalChanges() default DocumentNodeStoreService.DEFAULT_PREFETCH_EXTERNAL_CHANGES; @AttributeDefinition( name = "NodeStoreProvider role", @@ -168,7 +176,7 @@ "expression triggers a GC run every night at 2 AM: '" + DocumentNodeStoreService.CLASSIC_RGC_EXPR + "'." ) - String versionGCExpression() default ""; + String versionGCExpression() default DocumentNodeStoreService.DEFAULT_VER_GC_EXPRESSION; @AttributeDefinition( name = "Time limit for a Version GC run (in sec)", @@ -225,7 +233,7 @@ name = "DocumentNodeStore update.limit", description = "Number of content updates that need to happen before " + "the updates are automatically purged to the private branch.") - int updateLimit(); + int updateLimit() default DEFAULT_UPDATE_LIMIT; @AttributeDefinition( name = "Persistent Cache Includes", Index: oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java =================================================================== --- oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java (revision 1816118) +++ oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentMK.java (working copy) @@ -22,6 +22,7 @@ import static com.google.common.base.Suppliers.ofInstance; import static org.apache.jackrabbit.oak.commons.PathUtils.concat; import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreService.DEFAULT_JOURNAL_GC_MAX_AGE_MILLIS; +import static org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreServiceConfiguration.PROP_UPDATE_LIMIT; import static org.apache.jackrabbit.oak.plugins.document.util.MongoConnection.readConcernLevel; import java.io.InputStream; @@ -145,7 +146,7 @@ * Number of content updates that need to happen before the updates * are automatically purged to the private branch. */ - static final int UPDATE_LIMIT = Integer.getInteger("update.limit", 100000); + static final int UPDATE_LIMIT = Integer.getInteger("update.limit", Builder.DEFAULT_UPDATE_LIMIT); /** * The node store. @@ -561,6 +562,7 @@ public static final int DEFAULT_DIFF_CACHE_PERCENTAGE = 30; public static final int DEFAULT_CACHE_SEGMENT_COUNT = 16; public static final int DEFAULT_CACHE_STACK_MOVE_DISTANCE = 16; + public static final int DEFAULT_UPDATE_LIMIT = 100000; private DocumentNodeStore nodeStore; private Supplier documentStoreSupplier = ofInstance(new MemoryDocumentStore()); private String mongoUri; Index: oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java =================================================================== --- oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java (revision 1816118) +++ oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java (working copy) @@ -22,16 +22,8 @@ import static com.google.common.collect.Lists.newArrayList; import static java.util.Collections.emptyList; 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.lookupFrameworkThenConfiguration; -import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_CACHE_SEGMENT_COUNT; -import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_CACHE_STACK_MOVE_DISTANCE; -import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_CHILDREN_CACHE_PERCENTAGE; -import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_DIFF_CACHE_PERCENTAGE; import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_MEMORY_CACHE_SIZE; -import static org.apache.jackrabbit.oak.plugins.document.DocumentMK.Builder.DEFAULT_NODE_CACHE_PERCENTAGE; import static org.apache.jackrabbit.oak.plugins.document.NodeDocument.MODIFIED_IN_SECS_RESOLUTION; import static org.apache.jackrabbit.oak.spi.blob.osgi.SplitBlobStoreService.ONLY_STANDALONE_TARGET; import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean; @@ -70,7 +62,6 @@ import org.apache.jackrabbit.oak.api.jmx.CheckpointMBean; import org.apache.jackrabbit.oak.api.jmx.PersistentCacheStatsMBean; import org.apache.jackrabbit.oak.cache.CacheStats; -import org.apache.jackrabbit.oak.commons.PropertiesUtil; import org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats; import org.apache.jackrabbit.oak.plugins.document.util.Utils; import org.apache.jackrabbit.oak.spi.commit.ObserverTracker; @@ -112,6 +103,7 @@ import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; +import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -120,7 +112,6 @@ 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.metatype.annotations.Designate; import org.quartz.CronExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -130,8 +121,7 @@ */ @Component( configurationPolicy = ConfigurationPolicy.REQUIRE, - service = {}) -@Designate(ocd = Configuration.class) + configurationPid = {Configuration.PID}) public class DocumentNodeStoreService { private static final long MB = 1024 * 1024; @@ -142,16 +132,18 @@ static final boolean DEFAULT_SO_KEEP_ALIVE = false; static final String DEFAULT_PERSISTENT_CACHE = "cache,binary=0"; static final String DEFAULT_JOURNAL_CACHE = "diff-cache"; + static final boolean DEFAULT_CUSTOM_BLOB_STORE = false; public static final String CONTINUOUS_RGC_EXPR = "*/5 * * * * ?"; public static final String CLASSIC_RGC_EXPR = "0 0 2 * * ?"; public static final long DEFAULT_RGC_TIME_LIMIT_SECS = 3*60*60; // default is 3 hours - private static final String PREFIX = "oak.documentstore."; private static final String DESCRIPTION = "oak.nodestore.description"; static final long DEFAULT_JOURNAL_GC_INTERVAL_MILLIS = 5*60*1000; // default is 5min static final long DEFAULT_JOURNAL_GC_MAX_AGE_MILLIS = 24*60*60*1000; // default is 24hours + static final boolean DEFAULT_PREFETCH_EXTERNAL_CHANGES = false; private static final String DEFAULT_PROP_HOME = "./repository"; static final long DEFAULT_MAX_REPLICATION_LAG = 6 * 60 * 60; static final boolean DEFAULT_BUNDLING_DISABLED = false; + static final String DEFAULT_VER_GC_EXPRESSION = ""; /** * Revisions older than this time would be garbage collected @@ -169,55 +161,12 @@ */ static final long DEFAULT_BLOB_SNAPSHOT_INTERVAL = 12 * 60 * 60; - /** - * Name of framework property to configure Mongo Connection URI - */ - private static final String FWK_PROP_URI = "oak.mongo.uri"; - - /** - * Name of framework property to configure Mongo Database name - * to use - */ - private static final String FWK_PROP_DB = "oak.mongo.db"; - - /** - * Name of framework property to configure socket keep-alive for MongoDB - */ - private static final String FWK_PROP_SO_KEEP_ALIVE = "oak.mongo.socketKeepAlive"; - - // property name constants - values can come from framework properties or OSGi config - private static final String PROP_URI = "mongouri"; - private static final String PROP_DB = "db"; - static final String PROP_SO_KEEP_ALIVE = "socketKeepAlive"; - private static final String PROP_CACHE = "cache"; - private static final String PROP_NODE_CACHE_PERCENTAGE = "nodeCachePercentage"; - private static final String PROP_PREV_DOC_CACHE_PERCENTAGE = "prevDocCachePercentage"; - private static final String PROP_CHILDREN_CACHE_PERCENTAGE = "childrenCachePercentage"; - private static final String PROP_DIFF_CACHE_PERCENTAGE = "diffCachePercentage"; - private static final String PROP_CACHE_SEGMENT_COUNT = "cacheSegmentCount"; - private static final String PROP_CACHE_STACK_MOVE_DISTANCE = "cacheStackMoveDistance"; - private static final String PROP_BLOB_CACHE_SIZE = "blobCacheSize"; - private static final String PROP_PERSISTENT_CACHE = "persistentCache"; - private static final String PROP_JOURNAL_CACHE = "journalCache"; - public static final String PROP_PREFETCH_EXTERNAL_CHANGES = "prefetchExternalChanges"; - private static final String PROP_JOURNAL_GC_MAX_AGE_MILLIS = "journalGCMaxAge"; public static final String CUSTOM_BLOB_STORE = "customBlobStore"; - public static final String PROP_VER_GC_MAX_AGE = "versionGcMaxAgeInSecs"; - public static final String PROP_VER_GC_EXPRESSION = "versionGCExpression"; - public static final String PROP_VER_GC_TIME_LIMIT = "versionGCTimeLimitInSecs"; public static final String PROP_REV_RECOVERY_INTERVAL = "lastRevRecoveryJobIntervalInSecs"; - public static final String PROP_BLOB_GC_MAX_AGE = "blobGcMaxAgeInSecs"; - public static final String PROP_BLOB_SNAPSHOT_INTERVAL = "blobTrackSnapshotIntervalInSecs"; - private static final String PROP_HOME = "repository.home"; - public static final String PROP_REPLICATION_LAG = "maxReplicationLagInSecs"; public static final String PROP_DS_TYPE = "documentStoreType"; - private static final String PROP_BUNDLING_DISABLED = "bundlingDisabled"; - public static final String PROP_UPDATE_LIMIT = "updateLimit"; - public static final String PROP_ROLE = "role"; - private static final String PROP_JOURNAL_GC_INTERVAL_MILLIS = "journalGCInterval"; - private static enum DocumentStoreType { + private enum DocumentStoreType { MONGO, RDB; static DocumentStoreType fromString(String type) { @@ -248,11 +197,16 @@ private ComponentContext context; private Whiteboard whiteboard; private long deactivationTimestamp = 0; - private long maxReplicationLagInSecs; @Reference private StatisticsProvider statisticsProvider; + @Reference + private ConfigurationAdmin configurationAdmin; + + @Reference(service = Preset.class) + private Preset preset; + private boolean customBlobStore; private ServiceRegistration blobStoreReg; @@ -259,19 +213,19 @@ private BlobStore defaultBlobStore; - private volatile Configuration config; + private Configuration config; @Activate protected void activate(ComponentContext context, Configuration config) throws Exception { closer = Closer.create(); - this.config = config; + this.config = DocumentNodeStoreServiceConfiguration.create(context, + configurationAdmin, preset.configuration, config); this.context = context; whiteboard = new OsgiWhiteboard(context.getBundleContext()); executor = new WhiteboardExecutor(); executor.start(whiteboard); - maxReplicationLagInSecs = config.maxReplicationLagInSecs(); - customBlobStore = toBoolean(prop(CUSTOM_BLOB_STORE), false); - documentStoreType = DocumentStoreType.fromString(config.documentStoreType()); + customBlobStore = this.config.customBlobStore(); + documentStoreType = DocumentStoreType.fromString(this.config.documentStoreType()); registerNodeStoreIfPossible(); } @@ -294,37 +248,21 @@ } private void registerNodeStore() throws IOException { - String uri = PropertiesUtil.toString(prop(PROP_URI, FWK_PROP_URI), DEFAULT_URI); - String db = PropertiesUtil.toString(prop(PROP_DB, FWK_PROP_DB), DEFAULT_DB); - boolean soKeepAlive = PropertiesUtil.toBoolean(prop(PROP_SO_KEEP_ALIVE, FWK_PROP_SO_KEEP_ALIVE), DEFAULT_SO_KEEP_ALIVE); - - int cacheSize = toInteger(prop(PROP_CACHE), DEFAULT_CACHE); - int nodeCachePercentage = toInteger(prop(PROP_NODE_CACHE_PERCENTAGE), DEFAULT_NODE_CACHE_PERCENTAGE); - int prevDocCachePercentage = toInteger(prop(PROP_PREV_DOC_CACHE_PERCENTAGE), DEFAULT_NODE_CACHE_PERCENTAGE); - int childrenCachePercentage = toInteger(prop(PROP_CHILDREN_CACHE_PERCENTAGE), DEFAULT_CHILDREN_CACHE_PERCENTAGE); - int diffCachePercentage = toInteger(prop(PROP_DIFF_CACHE_PERCENTAGE), DEFAULT_DIFF_CACHE_PERCENTAGE); - int blobCacheSize = toInteger(prop(PROP_BLOB_CACHE_SIZE), DEFAULT_BLOB_CACHE_SIZE); - String persistentCache = getPath(PROP_PERSISTENT_CACHE, DEFAULT_PERSISTENT_CACHE); - String journalCache = getPath(PROP_JOURNAL_CACHE, DEFAULT_JOURNAL_CACHE); - int cacheSegmentCount = toInteger(prop(PROP_CACHE_SEGMENT_COUNT), DEFAULT_CACHE_SEGMENT_COUNT); - int cacheStackMoveDistance = toInteger(prop(PROP_CACHE_STACK_MOVE_DISTANCE), DEFAULT_CACHE_STACK_MOVE_DISTANCE); - boolean bundlingDisabled = toBoolean(prop(PROP_BUNDLING_DISABLED), DEFAULT_BUNDLING_DISABLED); - boolean prefetchExternalChanges = toBoolean(prop(PROP_PREFETCH_EXTERNAL_CHANGES), false); - int updateLimit = toInteger(prop(PROP_UPDATE_LIMIT), DocumentMK.UPDATE_LIMIT); - long journalGCMaxAge = toLong(context.getProperties().get(PROP_JOURNAL_GC_MAX_AGE_MILLIS), DEFAULT_JOURNAL_GC_MAX_AGE_MILLIS); + String persistentCache = resolvePath(config.persistentCache(), DEFAULT_PERSISTENT_CACHE); + String journalCache = resolvePath(config.journalCache(), DEFAULT_JOURNAL_CACHE); DocumentMK.Builder mkBuilder = new DocumentMK.Builder(). setStatisticsProvider(statisticsProvider). - memoryCacheSize(cacheSize * MB). + memoryCacheSize(config.cache() * MB). memoryCacheDistribution( - nodeCachePercentage, - prevDocCachePercentage, - childrenCachePercentage, - diffCachePercentage). - setCacheSegmentCount(cacheSegmentCount). - setCacheStackMoveDistance(cacheStackMoveDistance). - setBundlingDisabled(bundlingDisabled). - setJournalPropertyHandlerFactory(journalPropertyHandlerFactory). + config.nodeCachePercentage(), + config.prevDocCachePercentage(), + config.childrenCachePercentage(), + config.diffCachePercentage()). + setCacheSegmentCount(config.cacheSegmentCount()). + setCacheStackMoveDistance(config.cacheStackMoveDistance()). + setBundlingDisabled(config.bundlingDisabled()). + setJournalPropertyHandlerFactory(journalPropertyHandlerFactory). setLeaseCheck(!ClusterNodeInfo.DEFAULT_LEASE_CHECK_DISABLED /* OAK-2739: enabled by default */). setLeaseFailureHandler(new LeaseFailureHandler() { @@ -347,9 +285,9 @@ } } }). - setPrefetchExternalChanges(prefetchExternalChanges). - setUpdateLimit(updateLimit). - setJournalGCMaxAge(journalGCMaxAge). + setPrefetchExternalChanges(config.prefetchExternalChanges()). + setUpdateLimit(config.updateLimit()). + setJournalGCMaxAge(config.journalGCMaxAge()). setNodeCachePredicate(createCachePredicate()); if (!Strings.isNullOrEmpty(persistentCache)) { @@ -382,6 +320,10 @@ log.info("Connected to datasource {}", dataSource); } } else { + String uri = config.mongouri(); + String db = config.db(); + boolean soKeepAlive = config.socketKeepAlive(); + MongoClientURI mongoURI = new MongoClientURI(uri); if (log.isInfoEnabled()) { @@ -389,14 +331,14 @@ // might contain passwords log.info("Starting DocumentNodeStore with host={}, db={}, cache size (MB)={}, persistentCache={}, " + "journalCache={}, blobCacheSize (MB)={}, maxReplicationLagInSecs={}", - mongoURI.getHosts(), db, cacheSize, persistentCache, - journalCache, blobCacheSize, maxReplicationLagInSecs); + mongoURI.getHosts(), db, config.cache(), persistentCache, + journalCache, config.blobCacheSize(), config.maxReplicationLagInSecs()); log.info("Mongo Connection details {}", MongoConnection.toString(mongoURI.getOptions())); } - mkBuilder.setMaxReplicationLag(maxReplicationLagInSecs, TimeUnit.SECONDS); + mkBuilder.setMaxReplicationLag(config.maxReplicationLagInSecs(), TimeUnit.SECONDS); mkBuilder.setSocketKeepAlive(soKeepAlive); - mkBuilder.setMongoDB(uri, db, blobCacheSize); + mkBuilder.setMongoDB(uri, db, config.blobCacheSize()); log.info("Connected to database '{}'", db); } @@ -453,17 +395,14 @@ } if (blobStore instanceof BlobTrackingStore) { - final long trackSnapshotInterval = toLong(prop(PROP_BLOB_SNAPSHOT_INTERVAL), - DEFAULT_BLOB_SNAPSHOT_INTERVAL); - String root = getRepositoryHome(); - BlobTrackingStore trackingStore = (BlobTrackingStore) blobStore; if (trackingStore.getTracker() != null) { trackingStore.getTracker().close(); } ((BlobTrackingStore) blobStore).addTracker( - new BlobIdTracker(root, repoId, trackSnapshotInterval, (SharedDataStore) - blobStore)); + new BlobIdTracker(getRepositoryHome(), repoId, + config.blobTrackSnapshotIntervalInSecs(), + (SharedDataStore) blobStore)); } } @@ -545,7 +484,7 @@ } private boolean isNodeStoreProvider() { - return prop(PROP_ROLE) != null; + return !Strings.isNullOrEmpty(config.role()); } private boolean isContinuousRevisionGC() { @@ -562,8 +501,8 @@ } else { defaultExpr = ""; } - String expr = PropertiesUtil.toString(prop(PROP_VER_GC_EXPRESSION), ""); - if (expr.isEmpty()) { + String expr = config.versionGCExpression(); + if (Strings.isNullOrEmpty(expr)) { expr = defaultExpr; } // validate expression @@ -579,14 +518,11 @@ } private void registerNodeStoreProvider(final NodeStore ns) { - Dictionary props = new Hashtable(); - props.put(NodeStoreProvider.ROLE, prop(PROP_ROLE)); - nodeStoreReg = context.getBundleContext().registerService(NodeStoreProvider.class.getName(), new NodeStoreProvider() { - @Override - public NodeStore getNodeStore() { - return ns; - } - }, + Dictionary props = new Hashtable<>(); + props.put(NodeStoreProvider.ROLE, config.role()); + nodeStoreReg = context.getBundleContext().registerService( + NodeStoreProvider.class.getName(), + (NodeStoreProvider) () -> ns, props); log.info("Registered NodeStoreProvider backed by DocumentNodeStore"); } @@ -737,35 +673,15 @@ private void registerJMXBeans(final DocumentNodeStore store, DocumentMK.Builder mkBuilder) throws IOException { - addRegistration( - registerMBean(whiteboard, - CacheStatsMBean.class, - store.getNodeCacheStats(), - CacheStatsMBean.TYPE, - store.getNodeCacheStats().getName())); - addRegistration( - registerMBean(whiteboard, - CacheStatsMBean.class, - store.getNodeChildrenCacheStats(), - CacheStatsMBean.TYPE, - store.getNodeChildrenCacheStats().getName()) - ); + registerCacheStatsMBean(store.getNodeCacheStats()); + registerCacheStatsMBean(store.getNodeChildrenCacheStats()); for (CacheStats cs : store.getDiffCacheStats()) { - addRegistration( - registerMBean(whiteboard, - CacheStatsMBean.class, cs, - CacheStatsMBean.TYPE, cs.getName())); + registerCacheStatsMBean(cs); } DocumentStore ds = store.getDocumentStore(); if (ds.getCacheStats() != null) { for (CacheStats cacheStats : ds.getCacheStats()) { - addRegistration( - registerMBean(whiteboard, - CacheStatsMBean.class, - cacheStats, - CacheStatsMBean.TYPE, - cacheStats.getName()) - ); + registerCacheStatsMBean(cacheStats); } } @@ -786,13 +702,7 @@ ); if (mkBuilder.getBlobStoreCacheStats() != null) { - addRegistration( - registerMBean(whiteboard, - CacheStatsMBean.class, - mkBuilder.getBlobStoreCacheStats(), - CacheStatsMBean.TYPE, - mkBuilder.getBlobStoreCacheStats().getName()) - ); + registerCacheStatsMBean(mkBuilder.getBlobStoreCacheStats()); } if (mkBuilder.getDocumentStoreStatsCollector() instanceof DocumentStoreStatsMBean) { @@ -817,10 +727,9 @@ ); } + final long versionGcMaxAgeInSecs = config.versionGcMaxAgeInSecs(); + final long blobGcMaxAgeInSecs = config.blobGcMaxAgeInSecs(); - final long versionGcMaxAgeInSecs = toLong(prop(PROP_VER_GC_MAX_AGE), DEFAULT_VER_GC_MAX_AGE); - final long blobGcMaxAgeInSecs = toLong(prop(PROP_BLOB_GC_MAX_AGE), DEFAULT_BLOB_GC_MAX_AGE); - if (store.getBlobStore() instanceof GarbageCollectableBlobStore) { BlobGarbageCollector gc = store.createBlobGarbageCollector(blobGcMaxAgeInSecs, ClusterRepositoryInfo.getOrCreateId(nodeStore), @@ -859,6 +768,11 @@ } } + private void registerCacheStatsMBean(CacheStats cacheStats) { + addRegistration(registerMBean(whiteboard, CacheStatsMBean.class, + cacheStats, CacheStatsMBean.TYPE, cacheStats.getName())); + } + private void registerLastRevRecoveryJob(final DocumentNodeStore nodeStore) { long leaseTime = toLong(context.getProperties().get(PROP_REV_RECOVERY_INTERVAL), ClusterNodeInfo.DEFAULT_LEASE_UPDATE_INTERVAL_MILLIS); @@ -868,12 +782,10 @@ } private void registerJournalGC(final DocumentNodeStore nodeStore) { - long journalGCInterval = toLong(context.getProperties().get(PROP_JOURNAL_GC_INTERVAL_MILLIS), - DEFAULT_JOURNAL_GC_INTERVAL_MILLIS); addRegistration(scheduleWithFixedDelay(whiteboard, new JournalGCJob(nodeStore), jobPropertiesFor(JournalGCJob.class), - TimeUnit.MILLISECONDS.toSeconds(journalGCInterval), + TimeUnit.MILLISECONDS.toSeconds(config.journalGCInterval()), true/*runOnSingleClusterNode*/, true /*use dedicated pool*/)); } @@ -884,8 +796,8 @@ } Map props = jobPropertiesFor(RevisionGCJob.class); props.put("scheduler.expression", expr); - long versionGcMaxAgeInSecs = toLong(prop(PROP_VER_GC_MAX_AGE), DEFAULT_VER_GC_MAX_AGE); - long versionGCTimeLimitInSecs = toLong(prop(PROP_VER_GC_TIME_LIMIT), DEFAULT_RGC_TIME_LIMIT_SECS); + long versionGcMaxAgeInSecs = config.versionGcMaxAgeInSecs(); + long versionGCTimeLimitInSecs = config.versionGCTimeLimitInSecs(); addRegistration(scheduleWithFixedDelay(whiteboard, new RevisionGCJob(nodeStore, versionGcMaxAgeInSecs, versionGCTimeLimitInSecs), @@ -892,18 +804,10 @@ props, MODIFIED_IN_SECS_RESOLUTION, true, true)); } - private String prop(String propName) { - return prop(propName, PREFIX + propName); - } - - private String prop(String propName, String fwkPropName) { - return lookupFrameworkThenConfiguration(context, propName, fwkPropName); - } - - private String getPath(String propName, String defaultValue) { - String path = PropertiesUtil.toString(prop(propName), defaultValue); - if (Strings.isNullOrEmpty(path)) { - return path; + private String resolvePath(String value, String defaultValue) { + String path = value; + if (Strings.isNullOrEmpty(value)) { + path = defaultValue; } if ("-".equals(path)) { // disable this path configuration @@ -914,7 +818,7 @@ } private String getRepositoryHome() { - String repoHome = prop(PROP_HOME, PROP_HOME); + String repoHome = config.repository_home(); if (Strings.isNullOrEmpty(repoHome)) { repoHome = DEFAULT_PROP_HOME; } @@ -1058,4 +962,17 @@ props.put("scheduler.name", clazz.getName()); return props; } + + @Component( + service = Preset.class, + configurationPid = Configuration.PRESET_PID) + public static class Preset { + + Configuration configuration; + + @Activate + void activate(Configuration configuration) { + this.configuration = configuration; + } + } } Index: oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfiguration.java =================================================================== --- oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfiguration.java (nonexistent) +++ oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfiguration.java (working copy) @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.jackrabbit.oak.plugins.document; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.ImmutableMap; + +import org.apache.jackrabbit.oak.osgi.OsgiUtil; +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.ComponentContext; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Configuration for the {@link DocumentNodeStoreService}. Access is provided + * via {@link Configuration}, while internally the implementation considers + * entries in the following sequence: + *
    + *
  • Framework/system properties, potentially with mapped names. See + * {@link #frameworkPropertyNameFor(String)}.
  • + *
  • OSGi configuration for {@link DocumentNodeStoreService} with + * {@link Configuration#PID} if the property is set via the + * OSGi Configuration Admin.
  • + *
  • OSGi configuration with {@link Configuration#PRESET_PID}. The + * default value for a configuration entry will be provided if the + * OSGi Configuration Admin does not have an entry as a preset.
  • + *
+ */ +final class DocumentNodeStoreServiceConfiguration { + + /** + * Default framework property name prefix. + */ + private static final String DEFAULT_FWK_PREFIX = "oak.documentstore."; + + /** + * Name of framework property to configure Mongo Connection URI + */ + private static final String FWK_PROP_URI = "oak.mongo.uri"; + + /** + * Name of framework property to configure Mongo Database name + * to use + */ + private static final String FWK_PROP_DB = "oak.mongo.db"; + + /** + * Name of framework property to configure socket keep-alive for MongoDB + */ + private static final String FWK_PROP_SO_KEEP_ALIVE = "oak.mongo.socketKeepAlive"; + + /** + * Name of the framework property to configure the update limit. + */ + private static final String FWK_PROP_UPDATE_LIMIT = "update.limit"; + + private static final String PROP_DB = "db"; + private static final String PROP_URI = "mongouri"; + private static final String PROP_HOME = "repository.home"; + static final String PROP_SO_KEEP_ALIVE = "socketKeepAlive"; + static final String PROP_UPDATE_LIMIT = "updateLimit"; + + /** + * Special mapping of property names to framework properties. All other + * property names are mapped to framework properties by prefixing them with + * {@link #DEFAULT_FWK_PREFIX}. + */ + private static final Map FWK_PROP_MAPPING = ImmutableMap.of( + PROP_DB, FWK_PROP_DB, + PROP_URI, FWK_PROP_URI, + PROP_HOME, PROP_HOME, + PROP_SO_KEEP_ALIVE, FWK_PROP_SO_KEEP_ALIVE, + PROP_UPDATE_LIMIT, FWK_PROP_UPDATE_LIMIT + ); + + private DocumentNodeStoreServiceConfiguration() { + } + + static Configuration create(ComponentContext context, + ConfigurationAdmin configurationAdmin, + Configuration preset, + Configuration configuration) + throws IOException { + return (Configuration) Proxy.newProxyInstance( + DocumentNodeStoreServiceConfiguration.class.getClassLoader(), + new Class[]{Configuration.class}, + new ConfigurationHandler(context, configurationAdmin, preset, configuration) + ); + } + + private static String frameworkPropertyNameFor(String propertyName) { + String fwkPropName = FWK_PROP_MAPPING.get(propertyName); + if (fwkPropName == null) { + fwkPropName = DEFAULT_FWK_PREFIX + propertyName; + } + return fwkPropName; + } + + private static final class ConfigurationHandler implements InvocationHandler { + + private final ComponentContext context; + + /** + * The preset configuration. + */ + private final Configuration preset; + + /** + * The configuration taking precedence over the preset. + */ + private final Configuration configuration; + + private final Set configurationKeys; + + ConfigurationHandler(ComponentContext context, + ConfigurationAdmin configurationAdmin, + Configuration preset, + Configuration configuration) throws IOException { + this.context = checkNotNull(context); + this.preset = checkNotNull(preset); + this.configuration = checkNotNull(configuration); + this.configurationKeys = getConfigurationKeys(checkNotNull(configurationAdmin)); + } + + private static Set getConfigurationKeys(ConfigurationAdmin configurationAdmin) + throws IOException { + Set keys = new HashSet<>(); + org.osgi.service.cm.Configuration c = configurationAdmin.getConfiguration(Configuration.PID); + for (Object k : Collections.list(c.getProperties().keys())) { + keys.add(k.toString()); + } + return keys; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + String name = method.getName().replaceAll("_", "."); + Configuration c; + if (configurationKeys.contains(name)) { + c = configuration; + } else { + c = preset; + } + Object value = method.invoke(c); + // check if this is overridden by a framework property + String frameworkProp = OsgiUtil.lookup( + context.getBundleContext(), frameworkPropertyNameFor(name)); + if (frameworkProp != null) { + value = tryCoerce(frameworkProp, method.getReturnType(), value); + } + return value; + } + + private Object tryCoerce(String value, Class type, Object defaultValue) { + Object obj; + if (type == Boolean.class || type == boolean.class) { + obj = Boolean.parseBoolean(value); + } else if (type == Integer.class || type == int.class) { + try { + obj = Integer.parseInt(value); + } catch (NumberFormatException e) { + obj = defaultValue; + } + } else if (type == Long.class || type == long.class) { + try { + obj = Long.parseLong(value); + } catch (NumberFormatException e) { + obj = defaultValue; + } + } else if (type == String.class) { + obj = value; + } else { + obj = defaultValue; + } + return obj; + } + } +} Property changes on: oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfiguration.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfigurationTest.java =================================================================== --- oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfigurationTest.java (nonexistent) +++ oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfigurationTest.java (working copy) @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.jackrabbit.oak.plugins.document; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Hashtable; + +import org.apache.sling.testing.mock.osgi.junit.OsgiContext; +import org.junit.Rule; +import org.junit.Test; +import org.osgi.service.cm.ConfigurationAdmin; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DocumentNodeStoreServiceConfigurationTest { + + @Rule + public final OsgiContext context = new OsgiContext(); + + private ConfigurationAdmin configAdmin = context.getService(ConfigurationAdmin.class); + + private TestConfig configuration = new TestConfig(Configuration.PID); + + private TestConfig preset = new TestConfig(Configuration.PRESET_PID); + + @Test + public void defaultValues() throws Exception { + Configuration config = createConfiguration(); + assertEquals(DocumentNodeStoreService.DEFAULT_URI, config.mongouri()); + assertEquals(DocumentNodeStoreService.DEFAULT_DB, config.db()); + assertEquals(DocumentNodeStoreService.DEFAULT_SO_KEEP_ALIVE, config.socketKeepAlive()); + assertEquals(DocumentNodeStoreService.DEFAULT_CACHE, config.cache()); + assertEquals(DocumentMK.Builder.DEFAULT_NODE_CACHE_PERCENTAGE, config.nodeCachePercentage()); + assertEquals(DocumentMK.Builder.DEFAULT_PREV_DOC_CACHE_PERCENTAGE, config.prevDocCachePercentage()); + assertEquals(DocumentMK.Builder.DEFAULT_CHILDREN_CACHE_PERCENTAGE, config.childrenCachePercentage()); + assertEquals(DocumentMK.Builder.DEFAULT_DIFF_CACHE_PERCENTAGE, config.diffCachePercentage()); + assertEquals(DocumentMK.Builder.DEFAULT_CACHE_SEGMENT_COUNT, config.cacheSegmentCount()); + assertEquals(DocumentMK.Builder.DEFAULT_CACHE_STACK_MOVE_DISTANCE, config.cacheStackMoveDistance()); + assertEquals(DocumentNodeStoreService.DEFAULT_BLOB_CACHE_SIZE, config.blobCacheSize()); + assertEquals(DocumentNodeStoreService.DEFAULT_PERSISTENT_CACHE, config.persistentCache()); + assertEquals(DocumentNodeStoreService.DEFAULT_JOURNAL_CACHE, config.journalCache()); + assertEquals(DocumentNodeStoreService.DEFAULT_CUSTOM_BLOB_STORE, config.customBlobStore()); + assertEquals(DocumentNodeStoreService.DEFAULT_JOURNAL_GC_INTERVAL_MILLIS, config.journalGCInterval()); + assertEquals(DocumentNodeStoreService.DEFAULT_JOURNAL_GC_MAX_AGE_MILLIS, config.journalGCMaxAge()); + assertEquals(DocumentNodeStoreService.DEFAULT_PREFETCH_EXTERNAL_CHANGES, config.prefetchExternalChanges()); + assertEquals(null, config.role()); + assertEquals(DocumentNodeStoreService.DEFAULT_VER_GC_MAX_AGE, config.versionGcMaxAgeInSecs()); + assertEquals(DocumentNodeStoreService.DEFAULT_VER_GC_EXPRESSION, config.versionGCExpression()); + assertEquals(DocumentNodeStoreService.DEFAULT_RGC_TIME_LIMIT_SECS, config.versionGCTimeLimitInSecs()); + assertEquals(DocumentNodeStoreService.DEFAULT_BLOB_GC_MAX_AGE, config.blobGcMaxAgeInSecs()); + assertEquals(DocumentNodeStoreService.DEFAULT_BLOB_SNAPSHOT_INTERVAL, config.blobTrackSnapshotIntervalInSecs()); + assertEquals(null, config.repository_home()); + assertEquals(DocumentNodeStoreService.DEFAULT_MAX_REPLICATION_LAG, config.maxReplicationLagInSecs()); + assertEquals("MONGO", config.documentStoreType()); + assertEquals(DocumentNodeStoreService.DEFAULT_BUNDLING_DISABLED, config.bundlingDisabled()); + assertEquals(DocumentMK.Builder.DEFAULT_UPDATE_LIMIT, config.updateLimit()); + assertEquals(Arrays.asList("/"), Arrays.asList(config.persistentCacheIncludes())); + } + + @Test + public void presetMongoURI() throws Exception { + String uri = "mongodb://localhost:27017/test"; + addConfigurationEntry(preset, "mongouri", uri); + Configuration config = createConfiguration(); + assertEquals(uri, config.mongouri()); + } + + @Test + public void presetSocketKeepAlive() throws Exception { + boolean keepAlive = !DocumentNodeStoreService.DEFAULT_SO_KEEP_ALIVE; + addConfigurationEntry(preset, "socketKeepAlive", keepAlive); + Configuration config = createConfiguration(); + assertEquals(keepAlive, config.socketKeepAlive()); + } + + @Test + public void presetUpdateLimit() throws Exception { + int updateLimit = DocumentMK.Builder.DEFAULT_UPDATE_LIMIT / 2; + addConfigurationEntry(preset, "updateLimit", updateLimit); + Configuration config = createConfiguration(); + assertEquals(updateLimit, config.updateLimit()); + } + + @Test + public void presetPersistentCacheIncludes() throws Exception { + String[] includes = new String[]{"/foo", "/bar"}; + addConfigurationEntry(preset, "persistentCacheIncludes", includes); + Configuration config = createConfiguration(); + assertTrue(Arrays.equals(includes, config.persistentCacheIncludes())); + } + + @Test + public void presetOverridden() throws Exception { + String db = "test"; + addConfigurationEntry(preset, "db", "foo"); + addConfigurationEntry(configuration, "db", db); + Configuration config = createConfiguration(); + assertEquals(db, config.db()); + } + + @Test + public void presetResetToDefault() throws Exception { + String db = "test"; + addConfigurationEntry(preset, "db", db); + addConfigurationEntry(configuration, "db", DocumentNodeStoreService.DEFAULT_DB); + Configuration config = createConfiguration(); + assertEquals(DocumentNodeStoreService.DEFAULT_DB, config.db()); + } + + private Configuration createConfiguration() throws IOException { + return DocumentNodeStoreServiceConfiguration.create( + context.componentContext(), configAdmin, + preset.asConfiguration(), + configuration.asConfiguration()); + } + + private void addConfigurationEntry(TestConfig config, String key, Object value) + throws IOException { + config.put(key, value); + org.osgi.service.cm.Configuration c = configAdmin.getConfiguration(config.servicePid); + c.update(new Hashtable(config)); + } + + private static class TestConfig extends HashMap { + + final String servicePid; + + TestConfig(String servicePid) { + this.servicePid = servicePid; + } + + Configuration asConfiguration() { + return (Configuration) Proxy.newProxyInstance( + DocumentNodeStoreServiceConfigurationTest.class.getClassLoader(), + new Class[]{Configuration.class}, + this::getProperty); + } + + private Object getProperty(Object proxy, Method method, Object[] args) { + Object value = get(method.getName().replaceAll("_", ".")); + if (value == null) { + value = method.getDefaultValue(); + } + return value; + } + } +} Property changes on: oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceConfigurationTest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceTest.java =================================================================== --- oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceTest.java (revision 1816118) +++ oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreServiceTest.java (working copy) @@ -34,10 +34,12 @@ import org.apache.sling.testing.mock.osgi.junit.OsgiContext; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import static org.apache.jackrabbit.oak.plugins.document.Configuration.PID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -56,6 +58,8 @@ private final DocumentNodeStoreService service = new DocumentNodeStoreService(); + private final DocumentNodeStoreService.Preset preset = new DocumentNodeStoreService.Preset(); + private String repoHome; @Before @@ -62,6 +66,7 @@ public void setUp() throws Exception { assumeTrue(MongoUtils.isAvailable()); context.registerService(StatisticsProvider.class, StatisticsProvider.NOOP); + context.registerInjectActivateService(preset); MockOsgi.injectServices(service, context.bundleContext()); repoHome = target.newFolder().getAbsolutePath(); } @@ -123,7 +128,8 @@ @Test public void journalPropertyTracker() throws Exception { - MockOsgi.activate(service, context.bundleContext(), newConfig(repoHome)); + MockOsgi.setConfigForPid(context.bundleContext(), PID, newConfig(repoHome)); + MockOsgi.activate(service, context.bundleContext()); DocumentNodeStore store = context.getService(DocumentNodeStore.class); assertEquals(0, store.getJournalPropertyHandlerFactory().getServiceCount()); @@ -134,8 +140,9 @@ @Test public void setUpdateLimit() throws Exception { Map config = newConfig(repoHome); - config.put(DocumentNodeStoreService.PROP_UPDATE_LIMIT, 17); - MockOsgi.activate(service, context.bundleContext(), config); + config.put(DocumentNodeStoreServiceConfiguration.PROP_UPDATE_LIMIT, 17); + MockOsgi.setConfigForPid(context.bundleContext(), PID, config); + MockOsgi.activate(service, context.bundleContext()); DocumentNodeStore store = context.getService(DocumentNodeStore.class); assertEquals(17, store.getUpdateLimit()); } @@ -143,8 +150,9 @@ @Test public void keepAlive() throws Exception { Map config = newConfig(repoHome); - config.put(DocumentNodeStoreService.PROP_SO_KEEP_ALIVE, true); - MockOsgi.activate(service, context.bundleContext(), config); + config.put(DocumentNodeStoreServiceConfiguration.PROP_SO_KEEP_ALIVE, true); + MockOsgi.setConfigForPid(context.bundleContext(), PID, config); + MockOsgi.activate(service, context.bundleContext()); DocumentNodeStore store = context.getService(DocumentNodeStore.class); MongoDocumentStore mds = getMongoDocumentStore(store); DB db = MongoDocumentStoreTestHelper.getDB(mds); @@ -154,7 +162,8 @@ @Test public void continuousRGCDefault() throws Exception { Map config = newConfig(repoHome); - MockOsgi.activate(service, context.bundleContext(), config); + MockOsgi.setConfigForPid(context.bundleContext(), PID, config); + MockOsgi.activate(service, context.bundleContext()); boolean jobScheduled = false; for (Runnable r : context.getServices(Runnable.class, "(scheduler.expression=\\*/5 \\* \\* \\* \\* ?)")) { jobScheduled |= r.getClass().equals(DocumentNodeStoreService.RevisionGCJob.class); @@ -165,7 +174,8 @@ @Test public void continuousRGCJobAsSupplier() throws Exception { Map config = newConfig(repoHome); - MockOsgi.activate(service, context.bundleContext(), config); + MockOsgi.setConfigForPid(context.bundleContext(), PID, config); + MockOsgi.activate(service, context.bundleContext()); Runnable rgcJob = null; for (Runnable r : context.getServices(Runnable.class, null)) { if (r.getClass().equals(DocumentNodeStoreService.RevisionGCJob.class)) { @@ -181,7 +191,8 @@ public void persistentCacheExclude() throws Exception{ Map config = newConfig(repoHome); config.put("persistentCacheIncludes", new String[] {"/a/b", "/c/d ", null}); - MockOsgi.activate(service, context.bundleContext(), config); + MockOsgi.setConfigForPid(context.bundleContext(), PID, config); + MockOsgi.activate(service, context.bundleContext()); DocumentNodeStore dns = context.getService(DocumentNodeStore.class); assertTrue(dns.getNodeCachePredicate().apply("/a/b/c")); @@ -190,6 +201,42 @@ assertFalse(dns.getNodeCachePredicate().apply("/x")); } + @Ignore("SLING-7233") + @Test + public void preset() throws Exception { + MockOsgi.setConfigForPid(context.bundleContext(), + Configuration.PRESET_PID, + DocumentNodeStoreServiceConfiguration.PROP_SO_KEEP_ALIVE, true); + MockOsgi.activate(preset, context.bundleContext()); + + MockOsgi.setConfigForPid(context.bundleContext(), PID, newConfig(repoHome)); + MockOsgi.activate(service, context.bundleContext()); + + DocumentNodeStore store = context.getService(DocumentNodeStore.class); + MongoDocumentStore mds = getMongoDocumentStore(store); + DB db = MongoDocumentStoreTestHelper.getDB(mds); + assertTrue(db.getMongo().getMongoOptions().isSocketKeepAlive()); + } + + @Test + public void presetOverride() throws Exception { + MockOsgi.setConfigForPid(context.bundleContext(), + Configuration.PRESET_PID, + DocumentNodeStoreServiceConfiguration.PROP_SO_KEEP_ALIVE, true); + MockOsgi.activate(preset, context.bundleContext()); + + Map config = newConfig(repoHome); + config.put(DocumentNodeStoreServiceConfiguration.PROP_SO_KEEP_ALIVE, false); + MockOsgi.setConfigForPid(context.bundleContext(), PID, config); + + MockOsgi.activate(service, context.bundleContext()); + + DocumentNodeStore store = context.getService(DocumentNodeStore.class); + MongoDocumentStore mds = getMongoDocumentStore(store); + DB db = MongoDocumentStoreTestHelper.getDB(mds); + assertFalse(db.getMongo().getMongoOptions().isSocketKeepAlive()); + } + private static MongoDocumentStore getMongoDocumentStore(DocumentNodeStore s) { try { Field f = s.getClass().getDeclaredField("nonLeaseCheckingStore"); @@ -223,7 +270,8 @@ Map config) { assertFalse(new File(expectedPath).exists()); - MockOsgi.activate(service, context.bundleContext(), config); + MockOsgi.setConfigForPid(context.bundleContext(), PID, config); + MockOsgi.activate(service, context.bundleContext()); assertNotNull(context.getService(NodeStore.class)); // must exist after service was activated @@ -249,9 +297,9 @@ private void assertNoCachePath(String unexpectedPath, Map config) { assertFalse(new File(unexpectedPath).exists()); + MockOsgi.setConfigForPid(context.bundleContext(), PID, config); + MockOsgi.activate(service, context.bundleContext()); - MockOsgi.activate(service, context.bundleContext(), config); - assertNotNull(context.getService(NodeStore.class)); // must not exist after service was activated assertFalse(new File(unexpectedPath).exists());