blob: 9368d8ff3629bc9f0ccf2fc79f5d82db258649ea [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.jcr;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.matchers.JUnitMatchers.containsString;
import java.util.Set;
import javax.jcr.InvalidItemStateException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.jackrabbit.oak.commons.junit.LogCustomizer;
import org.apache.jackrabbit.oak.fixture.NodeStoreFixture;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import ch.qos.logback.classic.Level;
import com.google.common.collect.Sets;
@RunWith(Parameterized.class)
public class ConflictResolutionTest extends AbstractRepositoryTest {
// TODO add tests for all ConflictType types to observe generated logs
private final LogCustomizer logMergingNodeStateDiff = LogCustomizer
.forLogger(
"org.apache.jackrabbit.oak.plugins.commit.MergingNodeStateDiff")
.enable(Level.DEBUG).create();
private final LogCustomizer logConflictValidator = LogCustomizer
.forLogger(
"org.apache.jackrabbit.oak.plugins.commit.ConflictValidator")
.enable(Level.DEBUG).create();
public ConflictResolutionTest(NodeStoreFixture fixture) {
super(fixture);
}
@After
public void after() {
super.logout();
}
@Test
public void deleteChangedNode() throws RepositoryException {
// first INFO level
deleteChangedNodeOps("node0");
// DEBUG level
Set<String> mnsdLogs;
Set<String> cvLogs;
try {
logMergingNodeStateDiff.starting();
logConflictValidator.starting();
deleteChangedNodeOps("node1");
} finally {
mnsdLogs = Sets.newHashSet(logMergingNodeStateDiff.getLogs());
cvLogs = Sets.newHashSet(logConflictValidator.getLogs());
logMergingNodeStateDiff.finished();
logConflictValidator.finished();
}
// MergingNodeStateDif debug: NodeConflictHandler<DELETE_CHANGED_NODE>
// resolved conflict of type DELETE_CHANGED_NODE with resolution THEIRS
// on node jcr:content, conflict trace ^"/metadata/updated":"myself"
assertTrue(mnsdLogs.size() == 1);
assertThat(
"MergingNodeStateDiff log message must contain a reference to the handler",
mnsdLogs.toString(),
containsString("NodeConflictHandler<DELETE_CHANGED_NODE>"));
assertThat(
"MergingNodeStateDiff log message must contain a reference to the resolution",
mnsdLogs.toString(),
containsString("DELETE_CHANGED_NODE with resolution THEIRS"));
assertThat(
"MergingNodeStateDiff log message must contain a reference to the modified property",
mnsdLogs.toString(),
containsString("^\"/metadata/updated\":\"myself\"]"));
// ConflictValidator debug: Commit failed due to unresolved
// conflicts in /node1 = {deleteChangedNode = {jcr:content}}
assertTrue(cvLogs.size() == 1);
assertThat(
"ConflictValidator log message must contain a reference to the path",
cvLogs.toString(),
containsString("/node1 = {deleteChangedNode = {jcr:content}}"));
}
private void deleteChangedNodeOps(String node) throws RepositoryException {
getAdminSession().getRootNode().addNode(node).addNode("jcr:content")
.addNode("metadata");
getAdminSession().save();
Session session1 = createAdminSession();
Session session2 = createAdminSession();
try {
session1.getNode("/"+node+"/jcr:content").remove();
session2.getNode("/"+node+"/jcr:content/metadata").setProperty(
"updated", "myself");
session2.save();
try {
session1.save();
fail("Expected InvalidItemStateException");
} catch (InvalidItemStateException expected) {
assertThat(
"Expecting 'Unresolved conflicts in /"+node+"'",
expected.getMessage(),
containsString("OakState0001: Unresolved conflicts in /"+node+""));
}
} finally {
session1.logout();
session2.logout();
}
}
}