| /* |
| * 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.File; |
| import java.lang.reflect.Field; |
| import java.util.Map; |
| |
| import com.google.common.base.Supplier; |
| import com.google.common.collect.Maps; |
| import com.mongodb.MongoClient; |
| |
| import org.apache.commons.io.FilenameUtils; |
| import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore; |
| import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStoreTestHelper; |
| import org.apache.jackrabbit.oak.plugins.document.spi.JournalPropertyService; |
| 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.junit.OsgiContext; |
| import org.jetbrains.annotations.NotNull; |
| import org.junit.After; |
| import org.junit.Before; |
| 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; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.junit.Assume.assumeTrue; |
| import static org.mockito.Mockito.mock; |
| |
| public class DocumentNodeStoreServiceTest { |
| |
| @Rule |
| public final OsgiContext context = new OsgiContext(); |
| |
| @Rule |
| public final TemporaryFolder target = new TemporaryFolder(new File("target")); |
| |
| private final DocumentNodeStoreService service = new DocumentNodeStoreService(); |
| |
| private final DocumentNodeStoreService.Preset preset = new DocumentNodeStoreService.Preset(); |
| |
| private String repoHome; |
| |
| @Before |
| 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(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| MockOsgi.deactivate(service, context.bundleContext()); |
| MongoUtils.dropCollections(MongoUtils.DB); |
| } |
| |
| @Test |
| public void persistentCache() { |
| String persistentCache = FilenameUtils.concat(repoHome, "cache"); |
| assertPersistentCachePath(persistentCache, persistentCache, ""); |
| } |
| |
| @Test |
| public void journalCache() { |
| String journalCache = FilenameUtils.concat(repoHome, "diff-cache"); |
| assertJournalCachePath(journalCache, journalCache, ""); |
| } |
| |
| @Test |
| public void persistentCacheWithRepositoryHome() { |
| assertPersistentCachePath(FilenameUtils.concat(repoHome, "cache"), |
| "cache", repoHome); |
| } |
| |
| @Test |
| public void journalCacheWithRepositoryHome() { |
| assertJournalCachePath(FilenameUtils.concat(repoHome, "diff-cache"), |
| "diff-cache", repoHome); |
| } |
| |
| @Test |
| public void defaultPersistentCacheWithRepositoryHome() { |
| String persistentCache = FilenameUtils.concat(repoHome, "cache"); |
| assertPersistentCachePath(persistentCache, "", repoHome); |
| } |
| |
| @Test |
| public void defaultJournalCacheWithRepositoryHome() { |
| String journalCache = FilenameUtils.concat(repoHome, "diff-cache"); |
| assertJournalCachePath(journalCache, "", repoHome); |
| } |
| |
| @Test |
| public void disablePersistentCacheWithRepositoryHome() { |
| String persistentCache = FilenameUtils.concat(repoHome, "cache"); |
| assertNoPersistentCachePath(persistentCache, "-", repoHome); |
| |
| } |
| |
| @Test |
| public void disableJournalCacheWithRepositoryHome() { |
| String journalCache = FilenameUtils.concat(repoHome, "diff-cache"); |
| assertNoJournalCachePath(journalCache, "-", repoHome); |
| } |
| |
| @Test |
| public void journalPropertyTracker() throws Exception { |
| MockOsgi.setConfigForPid(context.bundleContext(), PID, newConfig(repoHome)); |
| MockOsgi.activate(service, context.bundleContext()); |
| DocumentNodeStore store = context.getService(DocumentNodeStore.class); |
| assertEquals(0, store.getJournalPropertyHandlerFactory().getServiceCount()); |
| |
| context.registerService(JournalPropertyService.class, mock(JournalPropertyService.class)); |
| assertEquals(1, store.getJournalPropertyHandlerFactory().getServiceCount()); |
| } |
| |
| @Test |
| public void setUpdateLimit() throws Exception { |
| Map<String, Object> config = newConfig(repoHome); |
| 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()); |
| } |
| |
| @Test |
| public void keepAlive() throws Exception { |
| Map<String, Object> config = newConfig(repoHome); |
| 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); |
| MongoClient client = MongoDocumentStoreTestHelper.getClient(mds); |
| assertTrue(client.getMongoClientOptions().isSocketKeepAlive()); |
| } |
| |
| @Test |
| public void continuousRGCDefault() throws Exception { |
| Map<String, Object> config = newConfig(repoHome); |
| 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); |
| } |
| assertTrue(jobScheduled); |
| } |
| |
| @Test |
| public void continuousRGCJobAsSupplier() throws Exception { |
| Map<String, Object> config = newConfig(repoHome); |
| 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)) { |
| rgcJob = r; |
| } |
| } |
| assertNotNull(rgcJob); |
| assertTrue(rgcJob instanceof Supplier); |
| assertNotNull(((Supplier) rgcJob).get()); |
| } |
| |
| @Test |
| public void persistentCacheExclude() throws Exception{ |
| Map<String, Object> config = newConfig(repoHome); |
| config.put("persistentCacheIncludes", new String[] {"/a/b", "/c/d ", null}); |
| MockOsgi.setConfigForPid(context.bundleContext(), PID, config); |
| MockOsgi.activate(service, context.bundleContext()); |
| |
| DocumentNodeStore dns = context.getService(DocumentNodeStore.class); |
| assertTrue(dns.getNodeCachePredicate().apply("/a/b/c")); |
| assertTrue(dns.getNodeCachePredicate().apply("/c/d/e")); |
| |
| assertFalse(dns.getNodeCachePredicate().apply("/x")); |
| } |
| |
| @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); |
| assertNotNull(mds); |
| MongoClient client = MongoDocumentStoreTestHelper.getClient(mds); |
| assertTrue(client.getMongoClientOptions().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<String, Object> 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); |
| MongoClient client = MongoDocumentStoreTestHelper.getClient(mds); |
| assertFalse(client.getMongoClientOptions().isSocketKeepAlive()); |
| } |
| |
| @Test |
| public void strictLeaseCheckMode() { |
| Map<String, Object> config = newConfig(repoHome); |
| MockOsgi.setConfigForPid(context.bundleContext(), PID, config); |
| MockOsgi.activate(service, context.bundleContext()); |
| |
| DocumentNodeStore dns = context.getService(DocumentNodeStore.class); |
| // strict is the default |
| assertEquals(LeaseCheckMode.STRICT, dns.getClusterInfo().getLeaseCheckMode()); |
| } |
| |
| @Test |
| public void lenientLeaseCheckMode() { |
| Map<String, Object> config = newConfig(repoHome); |
| config.put("leaseCheckMode", LeaseCheckMode.LENIENT.name()); |
| MockOsgi.setConfigForPid(context.bundleContext(), PID, config); |
| MockOsgi.activate(service, context.bundleContext()); |
| |
| DocumentNodeStore dns = context.getService(DocumentNodeStore.class); |
| assertEquals(LeaseCheckMode.LENIENT, dns.getClusterInfo().getLeaseCheckMode()); |
| } |
| |
| @NotNull |
| private static MongoDocumentStore getMongoDocumentStore(DocumentNodeStore s) { |
| try { |
| Field f = s.getClass().getDeclaredField("nonLeaseCheckingStore"); |
| f.setAccessible(true); |
| return (MongoDocumentStore) f.get(s); |
| } catch (Exception e) { |
| fail(e.getMessage()); |
| } |
| throw new IllegalStateException(); |
| } |
| |
| private void assertPersistentCachePath(String expectedPath, |
| String persistentCache, |
| String repoHome) { |
| Map<String, Object> config = newConfig(repoHome); |
| config.put("persistentCache", persistentCache); |
| config.put("journalCache", "-"); |
| assertCachePath(expectedPath, config); |
| } |
| |
| private void assertJournalCachePath(String expectedPath, |
| String journalCache, |
| String repoHome) { |
| Map<String, Object> config = newConfig(repoHome); |
| config.put("journalCache", journalCache); |
| config.put("persistentCache", "-"); |
| assertCachePath(expectedPath, config); |
| } |
| |
| private void assertCachePath(String expectedPath, |
| Map<String, Object> config) { |
| assertFalse(new File(expectedPath).exists()); |
| |
| MockOsgi.setConfigForPid(context.bundleContext(), PID, config); |
| MockOsgi.activate(service, context.bundleContext()); |
| |
| assertNotNull(context.getService(NodeStore.class)); |
| // must exist after service was activated |
| assertTrue(new File(expectedPath).exists()); |
| } |
| |
| private void assertNoPersistentCachePath(String unexpectedPath, |
| String persistentCache, |
| String repoHome) { |
| Map<String, Object> config = newConfig(repoHome); |
| config.put("persistentCache", persistentCache); |
| assertNoCachePath(unexpectedPath, config); |
| } |
| |
| private void assertNoJournalCachePath(String unexpectedPath, |
| String journalCache, |
| String repoHome) { |
| Map<String, Object> config = newConfig(repoHome); |
| config.put("journalCache", journalCache); |
| assertNoCachePath(unexpectedPath, config); |
| } |
| |
| private void assertNoCachePath(String unexpectedPath, |
| Map<String, Object> config) { |
| assertFalse(new File(unexpectedPath).exists()); |
| MockOsgi.setConfigForPid(context.bundleContext(), PID, config); |
| MockOsgi.activate(service, context.bundleContext()); |
| |
| assertNotNull(context.getService(NodeStore.class)); |
| // must not exist after service was activated |
| assertFalse(new File(unexpectedPath).exists()); |
| // also assert there is no dash directory |
| // the dash character is used to disable a persistent cache |
| assertFalse(new File(repoHome, "-").exists()); |
| } |
| |
| private Map<String, Object> newConfig(String repoHome) { |
| Map<String, Object> config = Maps.newHashMap(); |
| config.put("repository.home", repoHome); |
| config.put("db", MongoUtils.DB); |
| config.put("mongouri", MongoUtils.URL); |
| return config; |
| } |
| } |