blob: 0e475e21575257d45d0e8c8e6e52da37a7c90b62 [file] [log] [blame]
/*
* 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;
}
}